MATLABでLINE Notifyを利用する ～完全無欠版～

# はじめに
これまで数多のMATLAB/LINE notifyに関する[記](https://qiita.com/C2_now/items/f296e1d0fd9642727549)[事](https://qiita.com/C2_now/items/f9cf6ee5aa26592d72ab)を書いてきた(2個)。しかしこれら(2個)の記事、まだまだ至らぬ点がある。

この記事ではこれらの課題を解決した上で、完全無欠なMATLAB x LINE Notifyモジュールを構築する。

# 環境
- Windows10 1909
- MATLAB R2020a

# 課題
まずは課題の列挙。

- [メッセージがちょっと変](#メッセージがちょっと変)
- [スタンプが利用できない](#スタンプが利用できない)
- [(2個目の記事では特に)LINEを利用しただけで美しくない](#美しくない)

ではそれぞれ解決していこう。

<details><summary>解決編</summary><div>
## メッセージがちょっと変

これは2個目の記事の[コード](https://qiita.com/C2_now/items/f9cf6ee5aa26592d72ab#%E7%94%BB%E5%83%8F%E3%81%AE%E9%80%81%E4%BF%A1)で発生する問題。以下の画像を見てほしい。
![2020-12-28 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629180/e834bae9-e3f6-56e4-c419-d18a4b0b6783.png)
なんかいらん文字が入ってる(+とか=とか)。

これは`MultipartFormProvider`の使い方が原因だった。`MultipartFormProvider`の引数`Parts`には`ContentProvider`オブジェクトしか取れないものと思っていて、`message`パラメータ(LINE notifyのパラメータ)に`FormProvider`オブジェクトを渡していた。どうやら、`FormProvider`は与えられた文字列とパラメータを=と+でくっつけてしまうらしい。

`MultipartFormProvider`の[ドキュメント](https://jp.mathworks.com/help/matlab/ref/matlab.net.http.io.multipartformprovider-class.html)をよく読んでみると引数`Parts`に「その他のタイプ」を取れるとあった。
なんだ、`string`とか`char`も取れるじゃん。

というわけで書き換えるとこんな感じ。

```matlab
msg = "test message from MATLAB";
provider = io.MultipartFormProvider('message', msg);
```

実行するとこう。
![2020-12-28 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629180/104e309f-b32e-c9d4-8268-c36e9351879a.png)


## スタンプが利用できない

`stickerPackageId`パラメータ、`stickerId`(LINE notifyのパラメータ)にそれぞれスタンプパッケージIDとスタンプIDを渡してあげればよい。利用可能なスタンプは[ここ](https://devdocs.line.me/files/sticker_list.pdf)参照。購入したスタンプを自由に利用できるわけではないみたい。

なお、**スタンプだけを送ることはできない**(APIの制限)。必ずメッセージと一緒に送る必要がある。メッセージがない場合、400エラーを返されて終わるので気をつけよう。また、ID値は数値型ではなく**文字列型で与えてやる必要がある**(`string`や`char`)。

それを踏まえるとこんな感じ。

```matlab
msg = "test message from MATLAB";
provider = io.MultipartFormProvider('message', msg, 'stickerPackageId', string(4), 'stickerId', string(279));
```

実行するとこう。
![2020-12-28 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629180/36913bc4-389b-68ee-4d59-92f95fe4bcf6.png)

## 美しくない
さて、本題。というか本命なので、[次項](#完全無欠-MATLAB-x-LINE-Notifyモジュール)で解説する。
</div></details>

# 完全無欠 MATLAB x LINE Notifyモジュール
## 完全無欠の条件
どでかい風呂敷を広げるために、完全無欠の条件を予め定めておく。

- アクセストークンを関数内にベタ書きしない  
呼び出す側から設定したいので。
- 複数のアクセストークンに対応させる  
使い分けできると便利なので。
- 関数のオーバーロード  
`msg_send`とか`stamp_send`とか`img_send`みたいに使い分けするのは面倒。

でかい風呂敷のわりには案外少ないね。

## ではコードをドン

```matlab:MATLAB_LINE_notify.m
classdef MATLAB_LINE_notify < handle
    properties(Access = private)
        token
        uri
        header
        method
    end
    
    methods
        function obj = MATLAB_LINE_notify(t)
            obj.token = t;
            obj.uri = 'https://notify-api.line.me/api/notify';
            obj.header = matlab.net.http.HeaderField('Authorization', ['Bearer ', obj.token],...
                'Content-Type', 'multipart/form-data');
            obj.method = 'POST';
        end
        
        function status = notify(obj, msg, varargin)
            if nargin < 2
                msg = "This is a default message.";
            end
            switch nargin
                case 1
                    provider = matlab.net.http.io.MultipartFormProvider('message', msg);
                case 2
                    provider = matlab.net.http.io.MultipartFormProvider('message', msg);
                case 3
                    if class(varargin{1}) == "double"
                        sticker_ids = validate_stamp(varargin{1});
                        provider = matlab.net.http.io.MultipartFormProvider(...
                            'message', msg, 'stickerPackageId', string(sticker_ids(1)),...
                            'stickerId', string(sticker_ids(2)));
                    elseif or(class(varargin{1}) == "char", class(varargin{1}) == "string")
                        img_path = validate_img(varargin{1});
                        img = matlab.net.http.io.FileProvider(img_path);
                        provider = matlab.net.http.io.MultipartFormProvider(...
                            'message', msg, 'imageFile', img);
                    end
                case 4
                    if class(varargin{1}) == "double"
                        sticker_ids = validate_stamp(varargin{1});
                        img_path = validate_img(varargin{2});
                        img = matlab.net.http.io.FileProvider(img_path);
                    elseif or(class(varargin{1}) == "char", class(varargin{1}) == "string")
                        sticker_ids = validate_stamp(varargin{2});
                        img_path = validate_img(varargin{1});
                        img = matlab.net.http.io.FileProvider(img_path);
                    end
                    provider = matlab.net.http.io.MultipartFormProvider(...
                        'message', msg, 'imageFile', img,...
                        'stickerPackageId', string(sticker_ids(1)),...
                        'stickerId', string(sticker_ids(2)));
                otherwise
                    error("入力引数が多すぎます。")
            end
            request = matlab.net.http.RequestMessage(obj.method, obj.header, provider);
            resp = send(request, obj.uri);
            status = resp.StatusCode;
        end
    end
end

%%
function sticker_ids = validate_stamp(sticker_ids)
    if length(sticker_ids) ~= 2
        error("スタンプIDが不正です。スタンプパッケージID、スタンプIDの順になっているか確認してください")
    else
        if sticker_ids(1) > 4
            error("スタンプIDが不正です。スタンプパッケージID、スタンプIDの順になっているか確認してください")
        end
    end
end

function img_path = validate_img(img_path)
    name = strsplit(img_path, '.');
    if ~ismember(name(end), {'png', 'jpg', 'jpeg'})
        error("画像ファイルの拡張子が不正です。pngもしくはjpgであることを確認してください。")
    end
end
```

特に解説することはないかと思う。[pythonでやっておられた方](https://qiita.com/moriita/items/5b199ac6b14ceaa4f7c9)がオブジェクト指向で書かれていたので、それリスペクトでオブジェクト指向で書いてみた。実際こうすることで、複数のトークンに対応することが可能となっている。

本当はオーバーロードを使ってきれいに書きたかったんだけど、MATLABには僕の思っているような(C#とかにあるような)オーバーロードはなかった。そのため、`nargin`や`varargin`を使って泥臭く書いてる。

## 使い方
特に迷うこともないかと思うが、一応。

まずはこのファイルをパスの通っているフォルダに保存。使用時は以下のように。

```matlab:test_line_notify.m
token = 'hogehoge';
msg = "Hello world!";
img = 'test.png';

bot = MATLAB_LINE_notify(token);

bot.notify(msg, img, [1,14])
```

まずはトークンやらの設定。このクラスを呼び出すときに、コンストラクタでトークンを設定してやる。(ないと思うけど、)途中でトークンを変更するなら`bot.token='new token'`などとすればよい。通知を出したいときは、`notify`メソッドを呼べばOK。

実行するとこんな感じ。
![2020-12-28 (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629180/a8a5d3e7-6090-1aca-d105-e09cf5f68d45.png)
ここで使ってるtest.pngはおそらくmatlabが用意してくれてるデフォルトのやつで、現在のフォルダーに呼び出してるtest.pngがないときこれが出てくる。

# まとめ
本当はアドカレ記事の追記とするつもりだったけど、思ったよりでかい分量になったので新しく記事にした。でも、それに見合うくらいいいものができたんじゃないかな。

実は、記事執筆時点(2020/12/28 AM3:20現在)で`nargin`や`varargin`を使って泥臭く書くか(載せたコード)、`arguments`を使って名前と値のペア引数で書くべきか悩んでる。`arguments`を使ったほうがデフォルト値の設定など融通がききやすいけど、ペア引数だとメソッドの呼び出しのときの打つ文字数が多くなるなあ、などと。ひとまずはこれで完成ということにしておきますが、また更新したりするかもしれません。

あと、今回のコードは[Githubで公開](https://github.com/C2-now/MATLAB_LineNotify)してます。ぜひご利用ください。