プッシュメッセージのエラー条件&LINEBotをブロックしているユーザーや友達追加していないユーザーを判別する方法

#はじめに
プログラミング言語をキーワードにconnpassイベントを検索してその詳細情報をbotで送信するというClovaスキルを開発したのですが、その申請をした際に、「LINEが送信できなかった場合は、LINEが送信できなかったと案内する仕様としてください」というご指摘を頂きました。調べているうちに、プッシュメッセージ送信時のエラー条件が結構複雑なことがわかったのでちょっと纏めてみました。
※「connpassイベント検索(非公式)」という名前のbotを使用して検証を行なっています。
<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260292/00a932d0-186c-8c70-a654-03fe8eb20f4c.png">
#エラー条件
そんなことより早くbotをブロックしているユーザーや友達追加していないユーザーを判別する方法を教えろという方は[こちら](#botをブロックしているユーザーや友達追加していないユーザーを判別する方法)
##自分のuserIdを使用した場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260292/c1785bca-9cf3-1015-c84f-3ba25e0059d7.png)
LINEDevelopersにあるBotページの一番下に記載されている自分のuserIdを使用しました。

```terminal:req
curl -v -X POST https://api.line.me/v2/bot/message/push \
-H 'Content-Type:application/json' \
-H 'Authorization: Bearer {ACCESSTOKEN}' \
-d '{
    "to": "{userId}",
    "messages":[
        {
            "type":"text",
            "text":"Hello, world1"
        },
        {
            "type":"text",
            "text":"Hello, world2"
        }
    ]
}'
```
###結果
```json:res
{}
```
当たり前ですが200で正常に送信されました。
##userIdをランダムな文字列にした場合
userIdをClovaスキルの公式シミュレータで使用されるランダムな文字列にして送信してみました。

```terminal:req
curl -v -X POST https://api.line.me/v2/bot/message/push \
-H 'Content-Type:application/json' \
-H 'Authorization: Bearer {ACCESSTOKEN}' \
-d '{
    "to": "0tfHnB8aRgaB1BxUcHw8pQ",
    "messages":[
        {
            "type":"text",
            "text":"Hello, world1"
        },
        {
            "type":"text",
            "text":"Hello, world2"
        }
    ]
}'

```
###結果
```json
{"message":"The property, 'to', in the request body is invalid (line: -, column: -)"}
```
400エラーを吐きました。
##「Botをブロックした状態で」自分のuserIdを使用した場合
curlのコードやブロック以外の条件は[自分のuserIdを使用した場合](#自分のuserIdを使用した場合)と同じです。
###結果
```json:res
{}
```
Botをブロックしているのでメッセージは届いていませんが、200が返ってきています。
##他のプロバイダで取得した自分のuserIdを使用した場合
「connpassイベント検索(非公式)」とは別のプロバイダで取得した自分のuserIdを使用しました。

```req
curl -v -X POST https://api.line.me/v2/bot/message/push \
-H 'Content-Type:application/json' \
-H 'Authorization: Bearer {ACCESSTOKEN}' \
-d '{
    "to": "{otherProvider's MyUserId}",
    "messages":[
        {
            "type":"text",
            "text":"Hello, world1"
        },
        {
            "type":"text",
            "text":"Hello, world2"
        }
    ]
}'
```
###結果
```json:req
{"message":"Failed to send messages"}
```
400エラーを吐きました。
##同じプロバイダにある別botから取得した、connpassイベント検索（非公式）を追加していないユーザーのuserIdを使用した場合
「connpassイベント検索(非公式)」と同じプロバイダにある別botで取得した、connpassイベント検索（非公式）を追加していない友人のuserIdを使用しました。
###結果
```json:res
{}
```
しっかり200が返ってきています
##<font color="Red">【追記】</font>
同プロバイダのbotを一度も友達追加していないユーザーにプッシュメッセージを送ると400の```{"message":"Failed to send messages"}```を吐きます。
LIFFでuserIdを取得して、そこ宛にプッシュメッセージを送るなどの処理を行なっているbotは要注意です。LIFFの認証だけ承諾してbotを友達追加していない場合は400を吐きます。

1度目にこの検証をした際に、LINEDevelopersのbotのページに表示されているuserIdを使用して、botを友達追加する前にプッシュメッセージを送ったのですが、しっかり200が返ってきたので、友達追加されていなくても200を吐くものだと思っていました...。LINEDevelopersに登録しているものとは別のサブ垢を使用して、botを追加していない状態で（LIFFの認証だけ承認）、LIFFで取得したuserId宛にプッシュメッセージを送ってみたら見事400を吐きました...。とんでもない落とし穴...。
##纏めると
ブロックされていようが~~友達追加されてていなかろうが~~、自分のプロバイダに存在するuserIdを使用した場合はしっかり200が返ってきます。これではメッセージが相手に届いているのか分かりません。
#botをブロックしているユーザーや友達追加していないユーザーを判別する方法
さて、やっとこ本題に入ります。前述の通り、ブロックされていようが友達追加されてていなかろうが自分のプロバイダに存在するuserIdを使用した場合はしっかり200が返ってくるので、PUSHAPIを叩くだけでは判別することが出来ません。そこで、プロフィールを取得するAPIが、ブロックされているユーザーや友達追加されていないユーザーのuserIdで叩くとエラーを吐く仕様になっていることを利用しました。

```js:Node.js
try {
  const profile = await client.getProfile(userId);
} catch (e) {
  //友達追加されていないかブロックされていた時の処理
}
```
※上のコードは[line-bot-sdk-nodejs](https://github.com/line/line-bot-sdk-nodejs)を利用しています。また、awaitを使用しているため上のコードを使用する際はasyncで包んでご使用下さい。
※「 https://api.line.me/v2/bot/profile/{userId} 」 を叩けば良いだけなので、別にSDKを使わなくても大丈夫です。詳しいAPIの仕様は[リファレンス](https://developers.line.biz/ja/reference/messaging-api/#get-profile)をご参照ください。
#おまけ
冒頭で紹介したconnpassイベント検索スキルです。
https://clova.line.me/clova-ai/skillstore/skill/info.shinbunbun.skill.connpass