LoginSignup
6
5

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-12-27

はじめに

これまで数多のMATLAB/LINE notifyに関するを書いてきた(2個)。しかしこれら(2個)の記事、まだまだ至らぬ点がある。

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

環境

  • Windows10 1909
  • MATLAB R2020a

課題

まずは課題の列挙。

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

解決編

メッセージがちょっと変

これは2個目の記事のコードで発生する問題。以下の画像を見てほしい。
2020-12-28 (1).png
なんかいらん文字が入ってる(+とか=とか)。

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

MultipartFormProviderドキュメントをよく読んでみると引数Partsに「その他のタイプ」を取れるとあった。
なんだ、stringとかcharも取れるじゃん。

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

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

実行するとこう。
2020-12-28 (2).png

スタンプが利用できない

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

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

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

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

実行するとこう。
2020-12-28 (3).png

美しくない

さて、本題。というか本命なので、次項で解説する。

完全無欠 MATLAB x LINE Notifyモジュール

完全無欠の条件

どでかい風呂敷を広げるために、完全無欠の条件を予め定めておく。

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

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

ではコードをドン

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でやっておられた方がオブジェクト指向で書かれていたので、それリスペクトでオブジェクト指向で書いてみた。実際こうすることで、複数のトークンに対応することが可能となっている。

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

使い方

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

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

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
ここで使ってるtest.pngはおそらくmatlabが用意してくれてるデフォルトのやつで、現在のフォルダーに呼び出してるtest.pngがないときこれが出てくる。

まとめ

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

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

あと、今回のコードはGithubで公開してます。ぜひご利用ください。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5