QtアプリからIFTTT Webhooksのトリガを実行する

#1. 初めに

Qtアプリ(Qt5/C++)からIFTTTのWebhooksを叩いて、スマホにプッシュ通知するサンプルを紹介します。

IFTTTのWebhooksに関して、参考にしたサイトは[こちら](http://oita.oika.me/2017/09/16/ifttt-push-to-phone/)です。
クライアントであるQtアプリからHTTP POSTを実行するサンプルを作ってみたくて作成しました。
ソースコードは[github](https://github.com/argama147/qiita-IftttSample)に置いてあります。
IFTTTに関しては、元ネタ記事をほぼ同じですが、１つの記事に完結していたほうが読みやすいと思ったので、キャプチャをペタペタ貼って説明しています。
<font color="Red">
<-- 2018/05/07 追記 start -->
 **Qtのサンプルに関して** 
投稿して１日経ち、やはりvalue1()などにデフォルト実装がないのはおかしい（value1だけ設定してvalue2やvalue3を使用しない場合を考慮できていない）為、デフォルト実装を入れるよう実装を変えました。
混乱させてしまい申し訳ありません。
<-- 2018/05/07 追記 end -->

##1.1. 動作確認環境

* Qt Version 5.10.1
* Qtアプリの動作環境：Windows10
* スマホ(SO-02J(OSはAndroid8.0.0))
* [IFTTTアプリ(Android版)](https://play.google.com/store/apps/details?id=com.ifttt.ifttt&hl=ja)

#2. 動作

IFTTTやプログラムの説明をする前に、動作の説明をします。
Qtアプリを実行すると、
![qt_ifttt_app.png](https://qiita-image-store.s3.amazonaws.com/0/69678/835dbb4d-6861-1965-2698-2726ac02295a.png)
という画面が立ち上がります。
このアプリの「POST Request」ボタンを押下すると、
![qt_ifttt_app2.png](https://qiita-image-store.s3.amazonaws.com/0/69678/f9515387-4f95-fc1d-dff4-8cc6337eb48b.png)

が表示され、その後、POSTリクエストが正常に終了すると、

![qt_ifttt_app3.png](https://qiita-image-store.s3.amazonaws.com/0/69678/77e98a86-7313-9d6a-7ac4-b881a8ae2e23.png)

が表示されます。
スマホ側では、Notificationで、
![eventOccurred.png](https://qiita-image-store.s3.amazonaws.com/0/69678/b34759ac-027a-7435-c76d-c5203120cc6e.png)
のように通知されます。
「hello」というイベントが発生し、Value1、Value2、Value3にそれぞれ、test1、test2、test3というパラメータが設定されているのが確認できます。

#3. IFTTTの設定

ブラウザを開きます。（Internet ExplorerはNGのようです。本記事の操作はGoogle Chromeを使用しています）
[IFTTT](https://ifttt.com/)にSign Inして、アプレットの作成を行います。

###3.1. アプレット作成

メニューの「My Applets」から、「New Applet」（スマホアプリだと「＋」ボタン）を押下します。
すると、下記のような画面が表示されます。
![ifttt_newapplet.png](https://qiita-image-store.s3.amazonaws.com/0/69678/fa5d50b6-322d-9d99-e7e8-f9e8e67fe073.png)

###3.1.1. トリガ（this）

まず、thisを選択し、検索バーで「web」を入力して、
![ifttt_webhooks.png](https://qiita-image-store.s3.amazonaws.com/0/69678/6e783376-d919-6dc2-c7d1-826eda837f41.png)

を選択します。
次に、
![ifttt_webhooks2.png](https://qiita-image-store.s3.amazonaws.com/0/69678/9f680f40-1438-3c5b-dfb4-acfb2b56f7b4.png)

を選択します。

すると、イベント名を入力する画面になりますので、入力します。
ここでは、元ネタ記事と同じように「hello」とします。

![ifttt_event_name.png](https://qiita-image-store.s3.amazonaws.com/0/69678/6858ad67-d323-b9b3-e11c-79852f0088df.png)

入力後、「Create trigger」を押下します。

###3.1.2. アクション（that）

thatを選択後、検索バーで「noti」を入力し、

![ifttt_notifications.png](https://qiita-image-store.s3.amazonaws.com/0/69678/be7285b4-31ed-de82-0d2c-f1a4c7669d52.png)

を選択します。
次に、

![ifttt_notifications2.png](https://qiita-image-store.s3.amazonaws.com/0/69678/c6bdbe28-67d4-028d-0770-ea31e6659559.png)

を選択します。
次に、

![ifttt_notifications3.png](https://qiita-image-store.s3.amazonaws.com/0/69678/a0272f25-06ea-f1f2-f066-d12692a9e95d.png)

の画面で、Messageを下記のように入力します。

![ifttt_notification4.png](https://qiita-image-store.s3.amazonaws.com/0/69678/52e089c0-aeb6-3e9c-a7d3-dbcc8963f874.png)

使用できるパラメータは、「Add ingredient」ボタンを押下して入力するのが確実です。
入力後「Create action」ボタンを押下します。
その後、「Finish」ボタンを押下すればアプレット作成完了です。

#3.2. IFTTT webhooksの動作確認

作成したアプレットを選択し、左上のマークを押下します。

![webhooks1.png](https://qiita-image-store.s3.amazonaws.com/0/69678/9aa712da-d65f-cca8-7397-c35481802e7d.png)

次に、右上の「Documentation」ボタンを押下します。

![webhooks2.png](https://qiita-image-store.s3.amazonaws.com/0/69678/7599ad3a-7cee-32a6-a9ba-d24310debaa2.png)

すると、次の表示されます。


この画面は、どうやってWeb APIを叩いてよいのかの全てが説明され、試しに動作確認までできる、かなりわかりやすいページです。

![webhooks3.png](https://qiita-image-store.s3.amazonaws.com/0/69678/5b41fbdb-14c9-f925-e5bb-5155b498a8a4.png)

Webhooks APIのURLに設定する要素は下記の５つです。

* イベント名
* アクセスKey
* value1
* value2
* value3

※IFTTTのアプレット作成画面だとvalue1はValue1と表示されていますが、URLのパラメータとしては全部小文字の名前になります。

Your Key isのところにアクセスキーが記述されていますので、これをメモっておきます。（Qtのプログラムで使用します。）
ついでに、アプレットの動作確認をします。
赤字の部分に各パラメータに設定したい値を入力し、「Test It」ボタンを押下します。
すると、Qtアプリの代わりに、トリガが発生します。
アクションが実行されたことをスマホで確認したら、ブラウザ上での操作は終了です。

#4. Qtアプリの実装

IFTTTにアクセスするクラスをして作成したのは、

* IftttAccessManagerクラス
* IftttTestクラス（IftttAccessManagerクラスの子クラス）

の２つです。

#4.1. IftttAccessManagerクラス

IFTTT Webhooksにアクセスする仕組みを定義したクラスです。
IftttAccessManagerクラス自身では、下記コードのように、[QNetworkAccessManager](http://doc.qt.io/qt-5/qnetworkaccessmanager.html)を使用してPOSTしています。

プロジェクトファイルに

```IftttSample.pro
QT += network
```

の設定をすれば使用できるようになります。

```ifttttest.cpp
IftttAccessManager::IftttAccessManager(QObject *parent) : QObject(parent)
{
    m_networkManager = new QNetworkAccessManager(this);
}

void IftttAccessManager::requestPost()
{
    QUrl url("https://maker.ifttt.com/trigger/" + eventName()
             + "/with/key/" + webhooksKey());
    QUrlQuery postData;
    QString value1Str = value1();
    if (!value1Str.isEmpty()) {
        postData.addQueryItem("value1", value1Str);
    }
    QString value2Str = value2();
    if (!value2Str.isEmpty()) {
        postData.addQueryItem("value2", value2Str);
    }
    QString value3Str = value3();
    if (!value3Str.isEmpty()) {
        postData.addQueryItem("value3", value3Str);
    }
    url.setQuery(postData);
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader,
        "application/x-www-form-urlencoded");
    m_networkReply = m_networkManager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
    connect(m_networkReply, &QNetworkReply::finished, this,
            [=](){
        if (m_networkReply->error()) {
            emit postFinished((m_networkReply->errorString()));
        } else {
            emit postFinished("POST finished");
        }
        m_networkReply->deleteLater();
    });
```

カスタマイズできる５つのパラメータについては、デザインパターンのTemplate Methodパターンで、子クラスに設定させます。

```ifttttest.h
protected:
    virtual QString value1();
    virtual QString value2();
    virtual QString value3();
    virtual QString webhooksKey() = 0;
    virtual QString eventName() = 0;
```

※Template Methodパターンについて補足
Template Methodパターンでは子クラスに設定させる要素を必ずしもpure virtualにしなければならないわけではありません。virtualだけ付けて( =0 を付けずに)、親クラスでデフォルト設定を記述しておく場合もあります。今回の例ではvalue1()関数などがそれに当たります。（value1Str.isEmpty()で判定する為にデフォルト実装で空文字を返します）
Template Methodパターンは、必ずpure virtualにしなければならないと勘違いしている人（というかソースコード）を見かけたことがあるので、もしそういう人が近くにいたら教えてあげてほしいです。
pure virtual関数をOverrideした子クラスの関数の中身が全て同じだったという悲しいコードはあまり見たくないです。。。

#4.2. IftttTestクラス

今回動作確認用のパラメータを記述したクラスです。
webhooksKey()は、3.2.項でメモっておいたKeyを設定してください。

```ifttttest.cpp
QString IftttTest::value1()
{
    return "test1";
}

QString IftttTest::value2()
{
    return "test2";
}

QString IftttTest::value3()
{
    return "test3";
}

QString IftttTest::webhooksKey()
{
    return "Your Key";
}


QString IftttTest::eventName()
{
    return "hello";
}
```

##4.3. コードを記述している時に気づいたこと

[QNetworkAccessManager](http://doc.qt.io/qt-5/qnetworkaccessmanager.html)周りはQt4→Qt5に移行している内に仕様が変わったようです。
２点気づいたことがあります。

１つ目は、パラメータを設定する為のQUrl::addQueryItem()がDuplicatedになっており、Qt5.10.1ではビルドすら通らないことです。
代わりに、QUrlQuery::addQueryItem()を使用するようです。
詳しくは、[Stack Overflowの記事](https://stackoverflow.com/questions/2599423/how-can-i-post-data-to-a-url-using-qnetworkaccessmanager)を参照してください。

２つ目は、エラーハンドリングについてです。
最初、QNetworkAccessManager::finish()シグナルをキャッチし、引数のQUrlQuery *replyから
reply->error()でエラーかどうかを判断するつもりだったのですが、判断できませんでした。
「https」を「htps」に変更してエラー処理の動作確認を行いましたが、No Errorが返ってきます。
[ここ](http://memotiyou.blogspot.jp/2012/02/qt-c-qnetworkaccessmanager_4306.html)を見る限りできそうだったのですが、仕様が変わったのでしょうか？

そこで、本サンプルのようにpost()関数の返り値である[QNetworkReply](http://doc.qt.io/qt-5/qnetworkreply.html)から判断します。
このpost()関数の返り値で返ってきた[QNetworkReply](http://doc.qt.io/qt-5/qnetworkreply.html)は自分でdeleteする必要がある為、エラーかどうかを確認後、QNetworkReply::deleteLater()を呼んでいます。

#5. 最後に

いかがでしたでしょうか。
[QNetworkAccessManager](http://doc.qt.io/qt-5/qnetworkaccessmanager.html)を使用すれば、わりと簡単にクライアント側のPOSTのコードを書け、IFTTTを使用すれば、サーバ側の動作も簡単に実装することができることがわかりました。
ネットワークプログラムというと、どうしても（特にクライアントサイドのプログラムを書いている人には）ハードルが高いというイメージがありますが、作ってみればそう難しいことではないと思います。
Gitに登録してから、

* 可変パラメータは全て画面で設定できるようにしておけば、もっとかっこいいサンプルになったとか
* ~~よく考えれば、value1、value2、value3はpure virtualにしなくてよくね？ということに気づいたりとか・・・~~

まあ、この辺りで勘弁してくださいm(_ _"m)

読者様の参考になれば幸いです。