Rokiのチラ裏

学生による学習のログ

Line Notify APIで遊ぶ

仕事関連の作業や自作ライブラリ開発等の合間に、息抜きとして何かお家ハック的な別の事で遊びたかったのだが、少し良い遊び道具が見つかったので一つ。

Line NotifyというLine社提供のapiがある。これが何なのかについては、公式ページに詳しくあるので参照してほしい。

簡単に言えば、OAuth2 による認証を行い取得したアクセストークンからAPIを叩く事でグループ内に通知を送りつける事ができるAPIである。使い方は、開発ブログにあるように、curlを使えばとても簡単。

curl -X POST -H 'Authorization: Bearer [access_token]' -F 'message=foobar' https://  
notify-api.line.me/api/notify

APIに関するドキュメントもシンプルで整備されている。

さて、このAPIを使ってグループ内に通知を送る。用途は…まあなんというか個人的な話なのだが、執筆時でもうすぐ二年ぐらい付き合っている彼女と私は同棲をしている。付き合ってから一月に一度、互いにまあ初心を忘れぬよう、今日で何ヶ月付き合っているのだなという話をするのだが、最近になって私が忘れたりするようになった。これはいかんので、一月に一度、手元の端末に通知を送れたりできれば良い(?)。そういう用途を満たすには、適当にシェルスクリプトを記述して自宅サーバのcronに登録しておけば問題ない。ただまあ、どうせならバイナリにしておこうと思ったので、そうする。libcurlを使えば簡単だろう。

#include<iostream>
#include<fstream>
#include<iterator>
#include<boost/optional.hpp>
#include<curl/curl.h>

using namespace std::string_literals;

struct line_curler{
    explicit line_curler(const char* token,const char* str)
        :line_curler(token,str,boost::none,boost::none)
    {}

    explicit line_curler(
            const char* token,
            const char* str,
            boost::optional<const char*> image_url,
            boost::optional<const char*> thumbnail=
            "https://placehold.jp/150x150.png"
    )
        :mes_(str),
        post_data("message="s+mes_),
        thumbnail_(thumbnail?"imageThumbnail="s+std::string(thumbnail.value()):""s),
        image_url_(image_url?"imageFullsize="s+std::string(image_url.value()):""s),
        handler(curl_easy_init()),
        slist(nullptr)
    {
        std::string token_="Authorization: Bearer "s+std::string(token);

        slist=curl_slist_append(slist,token_.c_str());
        curl_easy_setopt(handler,CURLOPT_URL,"https://notify-api.line.me/api/notify");
        curl_easy_setopt(handler,CURLOPT_SSL_VERIFYPEER,false);
        curl_easy_setopt(handler,CURLOPT_HTTPHEADER,slist);
        curl_easy_setopt(handler,CURLOPT_POST,true);
        
        if(!image_url_.empty()){
            post_data+="&"s+thumbnail_+"&"s+image_url_;
            curl_easy_setopt(handler,CURLOPT_POSTFIELDS,post_data.c_str());
        }else{
            curl_easy_setopt(handler,CURLOPT_POSTFIELDS,post_data.c_str());
            curl_easy_setopt(handler,CURLOPT_POSTFIELDSIZE,post_data.size());
        }
    }
    
    void perform()const{curl_easy_perform(handler);}

    ~line_curler()
    {
        curl_slist_free_all(slist);
        curl_easy_cleanup(handler);
    }
private:
    const std::string mes_;
    std::string post_data;
    const std::string thumbnail_;
    const std::string image_url_;

    CURL* handler;
    curl_slist* slist;
};

std::string& trimer(std::string& str)
{
    const char* trimlist="\t\v\r\n";
    const std::string::size_type left=str.find_first_not_of(trimlist);
    if(left!=std::string::npos)
        str=str.substr(left,str.find_last_not_of(trimlist)-left+1);    
    return str;
}

struct token_reader{
    explicit token_reader(const char* filename):file_(boost::none)
    {
        std::ifstream ifs(filename);        
        if(ifs){
            std::string all_((std::istreambuf_iterator<char>(ifs)),std::istreambuf_iterator<char>());
            file_=trimer(all_);
        }
    }

    token_reader(const token_reader&)=default;
    token_reader(token_reader&&)=default;

    operator bool()const{return bool(file_);}
    operator const char*(){return file_?file_.value().c_str():nullptr;}
protected:
    boost::optional<std::string> file_;
};

template<class... Args>
auto exec_line_curl(Args&&... args) -> decltype(std::enable_if_t<sizeof...(args)<4>())
{
    line_curler lc(std::forward<Args>(args)...);
    lc.perform();
}

int main(const int argc,const char* argv[])
{
    if(argc<2)return -1;

    token_reader token("line_notify_api");

    if(token and argc==2)
        exec_line_curl(std::move(token),argv[1]);
    else if(token and argc==3)
        exec_line_curl(std::move(token),argv[1],argv[2]);
}

ビルドには-lcurlが必要。バイナリと同じディレクトリのline_notify_apiというファイル名からアクセストークンを読み込んで、単にapiを叩くだけだ。 以下のように使える。

./a.out 横から失礼致します。 http://www.dotup.org/uploda/www.dotup.org1173928.jpg

後は、このプログラムをcronで設定しておけば良いだろう。

Line Notify APIは、GithubやIFTTT、Mackerelなどと連携させる事ができるようなので、お家ハック的な意味ではまだ遊びようがありそうではある。取り敢えず、IFTTTから天気情報を流す設定はしてみた。

というわけで仕事に戻る