0 SIMの3か月無通信を避ける

0 SIMは、ソニーネットワークコミュニケーションズ株式会社が提供するSIMなのですが、データ通信が1か月500MBまでであれば、0円で済むという素晴らしいサービスです。
http://mobile.nuro.jp/0sim/

さらに、月々150円払えば、SMSも利用できるのも素晴らしいです。
特にSMSは、LINEなどアカウント登録にSMSが必要な場合に助かります。

ただし、注意点があります。3か月間、通信が全くないと自動的に解約となり、なおかつ、SIMカードを有償で返却する必要があります。

常時データ通信を使っていると月に500MBを超えないか心配になります。
かといって、基本Wifiを使っていると、3か月全くデータ通信しなくなっている可能性が心配になります。
ということで、Wifiを使っていても、3か月無通信とならないように、自動でデータ通信するように工夫したいと思います。
また、万が一その工夫がうまくいかなかった場合に備えて、LINEに警告メールを送るようにしたいと思います。2か月間通信がないと、ソニーネットワークコミュニケーションズからメール連絡がきますが、LINEの方が気づきやすいためです。

以下の技術を使います。

①3か月無通信とならないための自動通信の工夫
・IFTTT
②3か月無通信となった場合のLINEでの警告
・LINE Notify
・SwaggerによるRESTfulサーバ

# ①3か月無通信とならないための自動通信の工夫

普段使いはWifiであることを考えると、いったんWifiを切って、どこかのサイトにアクセスして、またWifiをOnに戻す必要があります。
これを人の手でやるとなると、やっぱり手間ですし忘れちゃいますよね。

そこで、IFTTT(イフト)の出番です。

まずは、スマホにIFTTTをインストールします。

以下の3つのAppletを作成します。

①一時的にWiFiをオフにする
　if
　this → Date & Time ： Every month on the
　then
　that → Android Device ： Turn off WiFi

②WifiがOffしたときに任意サイトにアクセスする
　if
　this → Android Device：Disconnects form any WiFi network
　then
　that → Webhooks ： Make a web request

③WiFiをオンにする
　if
　this → Date & Time ： Every month on the
　then
　that → Android Device ： Turn on WiFi


ちょっと例とともに補足します。

①一時的にWiFiをオフにする
WiFiをオフにするタイミングを指定します。1か月ごとに決まった時間に1回起動させます。スマホを使っていないときに勝手にやっててほしいので、例えば深夜の4時0分に設定します。（常時スマホをアダプタにつないでいる前提です。そうしないと、いつの間にか電池切れなんてことになりますので。。。）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/3cc75f3c-c74b-b14b-ab09-bc1eb73b936e.png)

②WifiがOffしたときに任意サイトにアクセスする

①でOffしたときに任意サイトにアクセスするように、WiFiがOffしたタイミングでWebhooksを起動させます。
Webhooksでアクセスする先はどこでもよいです。たとえば、https://www.google.com とします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/fa2ed9ff-14d0-7d64-a6f5-b6b7ac3645e1.png)
 
③WiFiをオンにする
今度は、②で用が済んだので、WiFiをオンに戻すタイミングを指定します。時間指定の「Date & Time」は15分刻みしか指定できないので、例えば深夜の4時15分にします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/95811604-053d-7307-45af-a4ee53187676.png)
 
一応、これで動くはず！！！
（WiFiがオフした瞬間にWebhooksしているので、本当にWiFiが切れた状態でデータ通信しているか不安ですが。。。まあ、WiFiオフ期間が15分もあるので、何か通信しているでしょうか。）


# ②3か月無通信となった場合のLINEでの警告

一応さっきのIFTTTの設定で、自動的にデータ通信しているはずですし、万が一はソニーネットワークコミュニケーションズからメールが来るので大丈夫だとは思うのですが、念のため無通信となっていないか見張って、まずかったらLINE通知してもらうようにしたいと思います。

仕組みとしてはこんな感じです。

１．さきほどWiFiオフしたときに www.google.com にアクセスしていましたが、これをこれから立ち上げるサイトに変えます。
２．立ち上げたサイトでは、アクセスを検知したら、現在時刻を最終アクセス時刻として覚えておきます。
３．覚えておいた最終アクセス時刻を見て、現在時刻との差分を見ることで経過時間をチェックします。これをcronで定期的に行います。
４．もし2か月以上経過していたら、危険なので、LINEに通知します。

１．Webhook先を変更する。

立ち上げるサイトのURLは後述します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/3bc2ac0a-a987-f08b-7205-6ea97f553768.png)

２．専用サイトを立ち上げる。

今回もSwaggerで立ち上げてみます。

以下も参考にしてください。
[SwaggerでRESTful環境を構築する](https://qiita.com/poruruba/items/72d8c85f8923bc78b918)

```swagger.yaml
  /ping:
    get:
      x-swagger-router-controller: routing
      operationId: ping
      responses:
        200:
          description: Success
          schema:
            type: object

  /ping-check:
    get:
      x-swagger-router-controller: routing
      operationId: ping-check
      responses:
        200:
          description: Success
          schema:
            type: object
```

/ping
　これがWebhooksでアクセスされて、最終アクセス時刻を記録するエンドポイントです。

/ping-check
　これは、cronから呼ばれて2か月経過していないかをチェックして、経過していたらLINE通知をします。

以下、実装です。

```index.js
const Response = require('../../helpers/response');
const fs = require('fs');
var fetch = require('node-fetch');
const { URLSearchParams } = require('url');

const FILEPATH = process.env.CHECK_FILE_PATH || './data/check.json';
const LINE_PERSONAL_ACCESS_TOKEN = process.env.LINE_PERSONAL_ACCESS_TOKEN || 【パーソナルアクセストークン】;

try {
    fs.statSync(FILEPATH);
} catch (error) {
    fs.writeFileSync(FILEPATH, JSON.stringify({}), 'utf8');
}

exports.handler = async (event, context, callback) => {
    console.log(event);
    if( event.path == '/ping'){
        try{
            var date = new Date();
            var check = {
                accessedAt: date.toLocaleString()
            };
            fs.writeFileSync(FILEPATH, JSON.stringify(check), 'utf8');

            callback(null, new Response({message: 'pong'}));
        }catch(error){
            callback(error);
        }
    }else if( event.path == '/ping-check' ){
        try{
            var check = JSON.parse(fs.readFileSync(FILEPATH, 'utf8'));
            console.log(check);
            if( !check.accessedAt ){
                var nowDate = new Date();
                var message = '\nまだ一度も通信していません。\n現在日時：' + nowDate.toLocaleString();
                await line_notify(message);
    
                callback(null, new Response({nowDate: nowDate.toLocaleString()}));
            }else{
                var nowDate = new Date();
                var lastDate = new Date(check.accessedAt);
                var diff = (nowDate.getTime() - lastDate.getTime()) / 1000;
    
                if( diff > 60 * 60 * 24 * (31 * 2 + 2 )){
                    var message = '\n0 SIMの無通信時間が残り1か月を切りました。\n現在日時：' + nowDate.toLocaleString() + '\n最終アクセス日：' + lastDate.toLocaleString();
                    await line_notify(message);
                }
    
                callback(null, new Response({lastDate: lastDate.toLocaleString(), nowDate: nowDate.toLocaleString()}));
            }
        }catch(error){
            callback(error);
        }
    }
};

function line_notify(message){
    return do_post_token('https://notify-api.line.me/api/notify', { message: message }, LINE_PERSONAL_ACCESS_TOKEN);
}

function do_post_token(url, body, token){
    var data = new URLSearchParams();
    for( var name in body )
        data.append(name, body[name]);

    return fetch(url, {
        method : 'POST',
        body : data,
        headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization' : 'Bearer ' + token }
    })
    .then((response) => {
        return response.json();
    });
}
```

ヘルパーも示しておきます。

```response.js
class Response{
    constructor(context){
        this.statusCode = 200;
        this.headers = {'Access-Control-Allow-Origin' : '*'};
        if( context )
            this.set_body(context);
        else
            this.body = "";
    }

    set_error(error){
        this.body = JSON.stringify({"err": error});
    }

    set_body(content){
        this.body = JSON.stringify(content);        
    }
    
    get_body(){
        return JSON.parse(this.body);
    }
}

module.exports = Response;
```

FILEPATH は、最終アクセス時刻を覚えておくファイル名です。JSONで保存します。
LINE_PERSONAL_ACCESS_TOKEN は、LINE Notifyページから取得しておく必要があります。以下で取得します。

１．スマホでLINEアプリを立ち上げる
２．LINE Notifyを友達追加します。公式アカウントから、検索で「LINE Notify」と入力すると出てきます。
３．LINE Notifyのページを開きます。右上にログインリンクがあるのでクリックしてLINEアカウントでのログインします。

https://notify-bot.line.me/ja/

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/af246823-2c42-d193-2976-ff777dda7d5e.png)
 
右上に自分の名前が出ているかと思いますがこれをクリックして「マイページ」を選択します。

 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/26f4e41c-df54-d3ad-e503-d3937a33726f.png)

アクセストークンの発行(開発者向け)、というのがありますので、「トークンを発行する」をクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/32fef67a-b41e-ece9-c3a6-0e546a53f95c.png)

自分だけLINE通知を受け取るのであれば、「1:1でLINE Notifyから通知を受け取る」でよいですし、もしトークルーム(トークグループ)で受け取りたい場合は、LINEアプリでそれを作ったのち、ここで選択します。
トークン名はたとえば、「0 SIM 無通信3か月の警告」とでもしておきます。
そうすると、赤字でトークンが発行されます。これを「パーソナルアクセストークン」と言うそうです。コピーボタンを押すなりして覚えておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/a7dfd000-ce9d-c33e-fac5-43a8bcf84b45.png)

「1:1でLINE Notifyから通知を受け取る」ではなくトークルームを選択した場合は、LINEアプリで、そのトークグループに、LINE Notifyをグループのメンバに追加しておく必要があるので忘れずにしておきましょう。

ということで、さきほどの赤字の部分を、LINE_PERSONAL_ACCESS_TOKEN に指定します。

３．cron設定する。

/ping-check を呼び出すシェルスクリプトを作ります。

```ping-check.sh
#!/bin/sh

curl https://【立ち上げたサイトURL】/ping-check >> /var/log/check_ping/check_ping.log

echo >> /var/log/check_ping/check_ping.log
```

実行権限を与えておきます。

> chmod +x check_ping.sh

これをcronから呼び出すようにします。

> crontab -e

たとえば、以下を追記。毎日午前5時0分にチェックを呼び出すようにしちえます。

> 0 5 * * * /opt/scripts/check_ping/check_ping.sh

４．2時間経過している場合はLINE通知する。

２．ですでに、実装済みです。/ping-chechの部分です。

もし2か月間/pingを呼び出していないと、以下のようなLINE通知が来ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/395aafe5-4608-f6d5-e3a8-e0cf926b5cd3.png)

もし、まだ一度もデータ通信のための/ping呼び出しをしていない状態で/ping-checkを呼びすと以下の通知が来ます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/4eebdb6d-e999-1924-ae1c-b4ea49831359.png)


（デバッグ中の画面なので、時刻が前後していたり、3か月もたっていないのに警告が出ていたりしていますが、気にしないでください）

以上です。