もぐてっく

人は1つ歳を取る度、1ビットづつ大きくなれると信じてた。

IFTTTにGoogle Apps Scriptを混ぜたらヤバい化学反応が起こった件

はじめに

2010年12月に彗星のごとく現れたWebサービス同士のマッシュアップサービスIFTTT。
「if (this) then (that)」と言うシンプル極まる構文で手軽にWebサービスが連携出来るのが特徴です。

IFTTTは様々なWebサービスをサポートしており、ちょっと挙げるだけでもTwitterEvernoteGoogle DriveSkypeなどの有名どころから、ナニコレ?ってものまで実に多彩です。

加えて、IFTTT独自のサービス群も魅力的です。
スマホ通知、Alexa・Google Homeの音声入力、スマホウィジェット(メモ、ボタン、写真)など。

それらを前述のシンプルな構文で自由に組み合わせられるのが、IFTTTの魅力だと思います。

IFTTTの弱点

シンプルさが売りのIFTTTなのですが、それがそのまま弱点にもなっているという印象です。

例えば「為替の情報を1時間に1回Pushbulletに通知したい」と言う比較的シンプルな要求はIFTTTでは実現できません。

為替の情報は「if (this) then (that)」のthisにFinanceサービスを指定すれば取得できますが、Financeサービスは「1時間に1回為替情報を報告する」と言うトリガーを持っていないからです。(1日1回トリガーはある。)

こんな感じで、イベントが発生するタイミングはthisのサービスに依存しており、ユーザーが自由に制御できません。

IFTTTではスマホウィジェットや各種スマートスピーカーを使って手動でトリガを掛けることもできますが、それ自身がthisを消費してしまうので、例えば下の様なアプレットは実現できません。

ボタンが押されたら(this) 明日の天気を(×指定不可) SMSに送信する(that)

シンプルさの引き換えに、少しでも凝ったアプレットは実現できない。これでは魅力が半減です。

IFTTTの弱点を克服する

そんな「なんでもは出来ないわ。出来ることだけ。」のIFTTTですが、その中に「Webhooks」と言う興味深いサービスがあります。

「Webhooks」はWebhookを送受信するサービスです。

と、言うことでIFTTTがサポートする以外の何かと連携が可能です。

これを上手く使えば、

為替の情報を1時間に1回Webhookする外部サービス -> Webhookを受信したら(this) Pushbulletに通知する(that)

ボタンが押されたら(this) Webhookを送信する(that) -> Webhookを受けて明日の天気をWebhookする外部サービス -> Webhookを受信したら(this) SMSに通知する(that)

が実現出来るということです。

外部サービスに為替や天気を取得する処理が必要な反面、IFTTTの豊富なthisやthatを組み合わせられるのは非常に魅力的です。

試してみる

と言うわけで、今回は「為替の情報を1時間に1回Webhookする外部サービス -> Webhookを受信したら(this) Pushbulletに通知する(that)」奴を作ってみようと思います。

「Webhookする外部サービス」にはGoogle Apps Script(GAS)を使います。

GASはGoogleさんのサーバでJavascriptを動かせるサービスで、Webhookを送受信するようなサービスが無料で作れちゃいます。
自宅サーバVPSを用意しなくてもインターネット上でちょっとした処理が動かせるので、とても使い勝手が良いです。

IFTTTのアプレット

thisの設定

(1)New Applet画面のthisをクリックする。
f:id:moguno:20180802155231p:plain

(2)Webhooksを選択する。
f:id:moguno:20180802155234p:plain

(3)Receive a web requestを選択する。
f:id:moguno:20180802155239p:plain

(4)イベント名を聞かれるので、今回はsample_triggerとする。
f:id:moguno:20180802155241p:plain

thatの設定

(1)New Applet画面のthatをクリックする。
f:id:moguno:20180802155245p:plain

(2)Pushbulletを選択する。
f:id:moguno:20180802155250p:plain

(3)Push a noteを選択する。
f:id:moguno:20180802155253p:plain

(4)Pushbulletに送信するテキストを設定する。
{{Value1}}がWebhook経由で渡されたデータになります。
画面上は白背景で表示されています。

f:id:moguno:20180802155259p:plain

Webhook用のURLの確認

WebhooksサービスのDocumentationボタンから、Webhook用のURLを確認しておきます。
f:id:moguno:20180802160529p:plain

https://maker.ifttt.com/trigger/sample_trigger/with/key/12345678が今回のURLになります。
f:id:moguno:20180802160520p:plain

GASのスクリプト

IFTTTのWebhookにPOSTする処理

// IFTTTにWebHookをPOSTする
function sendIFTTTWebHook(endpoint, value) {
  var message = {
    "value1":value
  };

  var options = {
    "method":"POST",
    "headers": {
      "Content-Type":"application/json"
    },
    "payload":JSON.stringify(message)
  };
  
  UrlFetchApp.fetch("https://maker.ifttt.com/trigger/” + endpoint + "/with/key/12345678", options)
}

為替情報を得る処理

// JPYUSDを得る
function getUSDJPY() {
  var text = UrlFetchApp.fetch("https://www.gaitameonline.com/rateaj/getrate").getContentText();
  var data = JSON.parse(text);
    
  var quotes = data["quotes"]
  
  var usdjpy = quotes.filter(function(item) {
    return item["currencyPairCode"] == "USDJPY";
  })[0];
  
  return usdjpy;
}

メイン処理

// メイン
function main() {
  var usdjpy = getUSDJPY()
  
  sendIFTTTWebHook("sample_trigger", "ドル円" + usdjpy["bid"]);
}

GASのスクリプトエディタの[編集] - [現在のプロジェクトのトリガー」で、main()が1時間周期で実行されるようにします。
f:id:moguno:20180802160953p:plain

おわりに

これで、為替の情報が1時間に1回Pushbulletに通知できるようになりました。

Pushbulletの扱いはIFTTTに任せているので、GAS側は非常に簡素なコードになっています。

今後「PushbulletじゃなくてTwitterに投稿したい!」となっても、IFTTTにthatをTwitterにしたアプレットを作るだけで対応が可能です。
また「1ドルが112円以上かつ113円以下の時だけ通知させる」みたいな複雑な条件は、GASでサクッと書いてしまえばいいでしょう。

IFTTTの手軽さとGASの自由度が融合した、とても扱いやすい仕組みが出来たと思います。

ところでこの仕組み、UNIXに於けるパイプラインに酷似していると思いませんか?

これこそ、2000年代からみんなが追い求めていた「Web2.0」の一つの到達点ではないかと思いました。

Ubuntu18.04でdnsmasqの起動に失敗する。

環境

  • Proxox Virtual Environment 5.2-2
  • ubuntu-18.04-standard_18.04-1_amd64.tar.gzで作ったコンテナ

apt-get install dnsmasqしたところ、DNSのポート(53/UDP)が既に使われているってことでdnsmasqの起動に失敗しました。

何者だ?と調べてみると、systemd-resolvedって子らしい。

systemd-resolvedは新しい名前解決の仕組みで、DNS、LLMNR、mDNSなんかを統一的に扱えるものの様です。

しかしながら、従来の/etc/resolv.confを直接参照してDNSに問い合わせに行くプログラム(そんな奴いるの?)のために、localhost(127.0.0.53)にスタブDNSサーバを立てつつ、それをネームサーバに指定した/etc/resolve.confを自動的に生成してくれる機能があります。
今回はこの機能がおせっかいをしていると。

幸い、Proxmox VEのUbuntu18.04イメージはsystemd-resolvedのスタブDNSを使わない*1ので、さくっと止めてしまいましょう。

/etc/systemd/resolved.conf

[Resolve]
#DNS=
#FallbackDNS=
#Domains=
#LLMNR=no
#MulticastDNS=no
#DNSSEC=no
#Cache=yes
DNSStubListener=no  ←コメントを外して"no"にする。

systemctl restart systemd-resolvedでスタブDNSがいなくなります。

*1:コンテナの起動時にProxmoxのGUIで指定したDNSのアドレスを/etc/resolve.confに出力している。

CentOS7にMastodonをインストールしたときにハマったこと

すごく何となくMastdonインスタンスを立てたくなったので、週末を3つほど使って悪戦苦闘してました。

インストールはこちらのサイトを元に進めました。
要点が纏まっていて助かりました。

qiita.com

しかしながら、最後の「http://<ホスト名>:3000/」で動作確認するところがどうしてもうまく行きませんでした。

[root@localhost live]# curl http://localhost:3000
<html><body>You are being <a href="https://localhost/">redirected</a>.</body></html>

「http://<ホスト名>:3000/」にアクセスすると「https://<ホスト名>/」にリダイレクトされるのですが、webコンテナはHTTP用の3000ポートしか外部にNATしていないので、リダイレクト先のHTTPSポート(433)にアクセスできないという状況です。

よくよく調べてみると、HTTPプロトコルには要求時にUpgrade:と言うヘッダを付けるとHTTP用のポートを使ってHTTPSで通信できる機能があるらしく、Mastodonはそれを採用しているようです。

動作確認に使ってたChromecurlは要求時にこのUpgrade:ヘッダを付けないので上手く行かなかったと言う話でした。

RFC 2817 - Upgrading to TLS Within HTTP/1.1

結論として、Webサーバのnginxはリバースプロキシとして動作するときにUpgrade:ヘッダを付けられるようなので、Mastodon公式サイトの設定例を元にnginxを立ち上げてみました。

documentation/Production-guide.md at master · tootsuite/documentation · GitHub

結果、無事サインアップの画面が表示されました。
ミッションコンプリ。

Mastodonをインストールするだけでも各種最先端技術に振り回されて満身創痍になってしまったので、運用はもうちょっと経験値が貯まってからにしようと思います。

CentOS7.4でdocker-compose runがUpdating Permissions...から進まなくなった

下記の組み合わせで、タイトルの現象が発生しました。

  • CentOS Linux release 7.4.1708 (Core)
  • Docker version 18.02.0-ce, build fc4de44
  • docker-compose version 1.19.0, build 9e633ef
[root@localhost live]# docker-compose --rm run web rake secret
Creating network "live_internal_network" with the default driver
Creating network "live_external_network" with the default driver
Starting live_db_1 ... done
Starting live_redis_1 ... done
Creating mastodon user (UID : 991 and GID : 991)...
Updating permissions...
(コンテナ内のファイルのfindとchownに明け暮れて全く帰ってこない。)

どうもDockerのストレージドライバoverlay2が原因の様です。
別のストレージドライバに切り替えればいい模様。

aufsが一般的らしいですが、今更だなぁと思ってbtrfsにしてみました。

CentOSを再インストール。インストール先の設定で/と/homeをbtrfsにするだけでOKでした。自動でストレージドライバをbtrfsにしてくれました。

mikutter 3.6のSpellのメモ

スペル(Spell)って?

まだよくわからぬ。
mikutter内の「動作」を柔軟に抽象化する仕組みだと思います。

仕様

スペルはdefspellと言うDSLとして定義されています。

$(MIKUTTER_DIR)/core/plugin/spell/spell.rb

defdsl :defspell do |spell_name, *constraint, condition: nil, &block|

spell_name

スペル名をシンボルで指定します。
例えば「投稿」は:composeです。

constraint

そのスペルに関わるモデルのslugを適当に並べて指定します。

みたいな。

condition

そのスペルが今発動できるか否かを返すlambdaを渡します。
procにはconstraintに指定したモデルのインスタンスが渡されます。
発動可能ならtrue、ダメならfalseを返しましょう。

block

スペルを発動した時に行う処理です。

スペルの呼び出し方

任意のPluginのインスタンスにスペル名と同名のメソッド(「compose()」の様な)があるので、それを呼びます。

引数には関係するモデルのインスタンス、オプションたち(ハッシュ)を適当な順番で渡せばいいみたいです。引数にnilがあっても大丈夫っぽいです。

$(MIKUTTER_DIR)/core/mui/gtk_postbox.rb

@posting = Plugin[:gtk].compose(
current_world,                                     # :twitterモデルのインスタンス
to_display_only? ? nil : @to.first,         # 送り先があるならそのモデルのインスタンス
body: text,                                           # オプション
visibility: @visibility                             # オプション
).next{

発動可能なスペルが存在するかを事前にチェックするには、「スペル名?」と言うメソッド(「compose?()」の様な)があるので、それを呼びます。

Plugin[:gtk].compose?(current_world, to_display_only? ? nil : @to.first, visibility: @visibility)

スペルの呼び出され方

$(MIKUTTER_DIR)/core/plugin/twitter/twitter.rb

defspell(:compose, :twitter,・・・・) do |twitter, body:, to: nil, **options|

ブロックの引数の種類に意味合いがあるので注意が必要です。
($(MIKUTTER_DIR)/core/plugin/spell/struct.rb"でやってます。)

  • twitter:普通の引数(req)
  • body:キーワード引数(keyreq)
  • to:デフォルト値ありのキーワード引数(key)
  • options:残りのキーワード引数(keyrest)

普通の引数(req)、デフォルト値ありの普通の引数(opt)

constraintに指定したモデルのインスタンス
(多かったり少なかったりすると例外が発生します*1

デフォルト値ありのキーワード引数(key)

スペルのオプションに同名のキーがある場合、それが代入されます。

キーワード引数(keyreq)

スペルのオプションに同名のキーがある場合、それが代入されます。同名のキーが無い場合は例外が発生します(*1)

残りのキーワード引数(keyrest)

オプションの内容が全部渡されます。

メモ

発動するスキルは次の条件で発見されます。

  • 呼び出し側が引数に指定したモデルと、スペル側のconstraintが完全に一致していること。⇒スペルのconditionが実行される。
  • conditionの結果がtrueであること。

*1:Plugin::Spell::ArgumentErrorが発生してるはずなんですがmikutterが落ちるわけでも--debugでもログが出るわけでもなく。謎です。

Windows Subsystem for LinuxのUbuntuでuim-mozcを使う

Windows Subsystem for Linux(WSL)が正式版になって、晴れてWindowsでmikutterが普通に使えるようになりました。

moguno.hatenablog.jp

しかしながら、WSLではfctixやibusと言った最近のインプットメソッドが動作しません。
なので、ちょいと古めのuimAnthyの組み合わせで使っていたのですが、このAnthyの変換精度がどうも微妙。

できればmozcの様な今どきの変換エンジンを使いたいなと。

実は、Ubuntu(と言うかDebian)にはmozcをuimで使うためのパッケージuim-mozcが存在します。

でも、uim-mozcは14.10(Utopic)でいったん廃止され(バグでも見つかった?)17.04(Zesty)で復活してる感じなので、WSLのUbuntuである16.04(Xenial)にはそれが存在していないというのが現実です。

この日曜日にいろいろ試して、Zestyからuim-mozcを借りてくることに成功したので、手順を書いておこうと思います。

準備

では、bashを起動してください。
sudoの連続は面倒なのでsudo -sでrootなシェルを起動しておきます。

sudo -s

aptの設定ファイルの調整

お好きなviで2つのファイルを新規作成しましょう。

/etc/apt/sources.list.d/zesty.list

zestyのリポジトリを検索対象に加えます。

deb http://ja.archive.ubuntu.com/ubuntu zesty main universe

/etc/apt/preferences.d/zesty

名指しで指定した場合のみzestyのパッケージを採用する様に優先度を調整します。

Package: *
Pin: release n=xenial
Pin-Priority: 990

Package: *
Pin: release n=zesty
Pin-Priority: 90

いつものアレ

apt-getでパッケージのデータベースを更新しておきます。

apt-get update

uim-mozcのインストール

それでは、いよいよuim-mozcのインストールです。
zesty名指しがポイントです。

apt-get install -t zesty uim-mozc

後は、uimをIMに使うように指定したアプリケーション(mikutterとか)を起動すれば、変換エンジンがmozcに変わっています。
日本語入力をONした後、ps -aux | grep mozcしてmozc_serverが常駐してたらOKだと思います。

moguno   28926  0.0  0.2 810056 16952 ?        Sl   22:55   0:00 /usr/lib/mozc/mozc_server

WSLを使ってWindowsでmikutterを動かす。

愛機MacBook Air(Mid 2012)の調子が悪くなったので、メインマシンを買い置きしていたThinkpad X1 Carbon(2016)に変更しました。*1

画面が広い!重量も軽い!Windows10も結構使いやすくなってきてる!と概ね満足だったのですが、唯一の懸念事項がmikutter。
mikutterはバージョンアップを重ねるごとにWindowsで動かすのが難しくなってきてるんで、これからWindowsで暮らしていく上で、伴侶であるみくったーちゃんと添い遂げることは出来るのかしらと。

そういえば、最近流行りのWSLでmikutterの動作報告があったなぁ。試してみるかと設定してみました。

結論としてはすごくいい感じ。macOSX11版)と同等の使い勝手が得られました。

f:id:moguno:20171008151829p:plain:w400

WSLのインストール

ここはいろんなサイトで手順が公開されているので端折ります。

(1)コルタナたんに「更新」と伝え「更新プログラムのチェック」を起動する。
「開発者向け」にある「開発者モード」を有効にする

(2)コルタナたんに「コントロール」と伝え「コントロールパネル」を起動する。
「プログラムと機能」の「Windowsの機能の有効化または無効化」から
Windows Subsystem for Linux(Beta)」をインストールする。

(3)コルタナたんに「bash」と伝え「Bash on Windows on Windows」を起動する。
Ubuntuのインストールが始まる。)

コルタナたんは仕事のできる子。

(4)bashが起動したら、環境を最新化しておく。

sudo apt-get update
sudo apt-get dist-upgrade

mikutterのインストール

今回は手抜きでUbuntuリポジトリにいるmikutterを使います。腕に覚えがある人はgitで最新版を取ってきてインストールしてください。なんだかんだでUbuntuなので、ハマり要素はかなり少ないと思います。

sudo apt-get install mikutter

日本語フォントのインストール

こちらを参考にしてGoogle謹製のnotoフォントをインストールします。

ぽぬぽぬ: Bash on Ubuntu on Windows + XサーバでLXDEを起動

ついでに絵文字(ttf-ancient-fonts)も入れておきましょう。

sudo apt-get install noto-fonts-hinted noto-fonts-cjk ttf-ancient-fonts

日本語入力システムのインストール

ここではちょっと懐かしいuimanthyをインストールします。*2

あれやこれやZakki: bash on ubuntu on windows(WSL)でGUIを立ち上げて日本語入力までやる

sudo apt-get install uim uim-xim uim-anthy

ブラウザ起動シェルスクリプトの作成

mikutterには、アカウントの認証時やURLをクリックしたときに起動するWebブラウザが必要です。
WSL側にfirefoxとかをインストールしてもいいのですが、せっかくのWindowsなのでEdgeとかIEを使いたいなと。

WSLでは、Windows用のプログラムを拡張子.exeまで含めて指定すると、そのプログラムが起動できるので、それでサクッと解決・・・と思ったのですが、EdgeもIEも実は.exeファイルは存在せずシェルエクステンションみたいな概念に昇華されてしまっているので、この方法はとれません。

なので、コマンドプロンプト(cmd.exe)を経由して、Windowsの既定のブラウザを起動するようにします。

/opt/mikutter-wsl/bin/start

#!/bin/sh
ESCAPED_URI=`echo "$1" | sed s/\&/^\&/g`
cmd.exe /C start "$ESCAPED_URI"

実行権限を付けておきましょう。

chmod 755 /opt/mikutter-wsl/bin/start

Xサーバのインストール

Windows用のXサーバはけっこう豊富に存在しますが、ここではVcXsrvを選択しました。理由はたまたま目についたからです。
この子どうやら高速なことで有名みたいです。実際サクサク動きます。

https://sourceforge.net/projects/vcxsrv/

VcXsrvの起動スクリプトの作成

VcXsrvには起動時に任意のコマンドを実行する機能があるので、mikutterをダイレクトに起動するようにします。
下記のXMLファイルを作成します。LocalProgramがキモの部分です。

mikutter.xlaunch

<?xml version="1.0" encoding="UTF-8"?>
<XLaunch WindowMode="MultiWindow"
ClientMode="StartProgram"
LocalClient="True"
Display="0"

LocalProgram="bash -c &quot;DISPLAY=:0 GTK_IM_MODULE=uim mikutter&quot; &gt; /dev/null 2&gt;&amp;1"

RemoteProgram="xterm"
RemotePassword=""
PrivateKey=""
RemoteHost=""
RemoteUser=""
XDMCPHost=""
XDMCPBroadcast="False"
XDMCPIndirect="False"
Clipboard="True"
ClipboardPrimary="True"
ExtraParams=""
Wgl="True"
DisableAC="False"
XDMCPTerminate="False"/>

実体参照が入ってて分かりにくいですが、デコードするとこんな感じになります。

bash -c "DISPLAY=:0 GTK_IM_MODULE_uim mikutter" > /dev/null 2>&1

標準出力、標準エラー出力を/dev/nullに捨てるのがポイントです。
そうしないとmikutterがコンソールにwarningとか出力するたびに、VcXsrvが「大丈夫?コマンド打つ?」ってダイアログを出してくるので。

起動してみる

さっき作ったmikutter.xlaunchをダブルクリックしてください。いつものmikutterが起動してくると思います。

f:id:moguno:20171008151850p:plain:w400

mikutterが起動したらさっき作ったブラウザを起動するシェルスクリプトを設定します。

(1)ウインドウ右下のネギレンチボタンを押す。
(2)「表示」の「URLを開く方法」に「/opt/mikutter-wsl/bin/start」と入力して下さい。

f:id:moguno:20171008151914p:plain:w400

これで完成です。Windowsでもレッツておくれ!

なお、日本語入力の切り替えはShift + スペースで行います。慣れましょう。

*1:使用頻度の低い公務用PCをスライドさせた感じっす。

*2:トレンディな日本語入力システムである(ibus|fcitx) + mozcは動きませんでした。Macでも定番のX用日本語入力システムはuimだったと思うので、そんなものなのでしょう。