LINE BOTを使ってSNSにいるおじさんと会話をする

## はじめに
突然ですが「SNSにいるおじさん」ってご存知ですか？
そんな言葉知らないよ〜、おじさんでもSNSは使うでしょ〜
という意見が出ますが、具体的なことは[こちら](https://qiita.com/tsugu_maru_san/private/a45d2709278fd855ef3a)を参考にしてください。

このおじさんの存在を知ったきっかけは、大学のサークルの先輩同士がTwitter上で
おじさんLINEごっこをしていて、

**この文章実際におじさんからきたらなんだか気持ち悪い...でも...見てる分には面白い**

と思ったので、SNSにいるおじさんと会話ができる需要は0のLINE BOTを作りました！！！！

こんなの
<img width="320" alt="S__92749881.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/146512/63c9c9fd-b7fb-0c76-ef90-2885d1cfa585.jpeg">


* たまには目的ありきからのモノづくりじゃなくてもいいじゃん

ということが伝わると幸いです。

## BOTを作った後付けの目的
* ちょっとおかしいかな？？？を客観視することで、自分が同じことを他の人にしないようにセーブする。
* 自分がもし同じことをしていたら「あ、この言い方は相手との距離を少し遠ざけてしまうんだ」を自覚するきっかけづくり
* 平たくいうと言葉選びのコミュニケーションを考え直すきっかけのものにする

## SNSにいるおじさんとは
[こちらのサイト](https://kogusoku.com/archives/2939)を参考にここでは以下のように定義しました。

* 絵文字、顔文字が多い
* 相手の子を「〜チャン」と呼ぶ
* 謎の漢字変換
* 聞かれてもないけど自分のことを語る
* 文章が長い
* やたらとご飯や遊びに誘う


FacebookやLINE等色々とありますが、
今回はLINEとメールにフォーカスを当てます。

## 開発環境
* サーバー：Heroku
* 開発言語：Node.js(8.11.3)
* API： LINE Developers API

## LINE Developersに登録
[こちらの記事](https://qiita.com/nkjm/items/38808bbc97d6927837cd)　を参考にLINE Developersに登録します。

### Channelの作成
[LINE Developers](https://developers.line.biz/ja/)にアクセスし、LINEでログインを行います。
LINEアカウントとビジネスアカウントどっち？って聞かれますが、今回はLINEアカウントにします。

#### 新規プロバイダー作成
ログインが完了したら、プロバイダーリストが表示されると思うので、
プロバイダーが1つも表示されていない場合は、「新規プロバイダー作成」のボタンを押下します。
プロバイダーの新規作成では、任意のプロバイダー名をつけましょう。

※ここのプロバイダーとは「個人・企業」を指します。

#### 新規チャンネル作成
プロバイダーの作成が完了したら、プロバイダーの画面に遷移します。
そこに「新規チャンネル作成」というカードがあると思うので、そちらをクリックします。

![スクリーンショット 2018-12-21 20.10.34.png](https://qiita-image-store.s3.amazonaws.com/0/146512/46045170-ca77-c67d-67eb-5d41d77df408.png)

新規作成をしたら、[Messaging API」を選択します。
![スクリーンショット 2018-12-21 20.11.38.png](https://qiita-image-store.s3.amazonaws.com/0/146512/6d0ed199-936b-ae2c-94f0-6d327ab8b479.png)

新規チャネル作成のページになるので、以下を入力しました。

```
アプリ名: 任意のアプリ名
アプリ説明: 任意のアプリ説明
プラン: Developer Trial
大業種: 任意のもの
小業種: 任意のもの
メールアドレス: ご自身のメールアドレス
```

上記を入力したらLINE@の利用規約・Messaging APIの利用規約に同意にチェックをし、作成をします。


ここで完了ではありません！！！！！！！

作成したチャネルを選択し、`Channel Secret`を再発行します。
これをすることによりアクセストークンが発行され、Messaging APIを呼び出す事ができます。
残りは以下のように設定します。

```
メッセージ送受信設定
Webhook送信: 利用する

LINE@機能の利用
自動応答メッセージ: 利用しない
友達追加時あいさつ: 利用しない

```

ここまででほぼ完了ですが、このページは度々登場するので、
ページは開いた状態で、作業を進めます。

## BOT本体を動かす
### BOT本体の開発
* ディレクトリの作成
今回作るディレクトリは`sns-ojisan`とします。

```
$ {任意のディレクトリ} mkdir sns-ojisan
$ cd sns-ojisan/
```

* package.jsonの作成
    * BOTを動かすためにライブラリを使用するため、package.jsonを作成します。

```
$ npm init --yes
```
* BOTを動かすために必要なライブラリをインストール
    * 各種必要なライブラリをインストールします。今回使用したライブラリは以下です。
        * crypto
        * express
        * @line/bot-sdk
        * request
    * 以下は`@line/bot-sdk`をインストールする例です。

```
npm install --save express @line/bot-sdk
```

ここで、今回BOTを動かすためのindex.jsを作成し以下を記述します。

```
$ touch index.js
```


```index.js
// -----------------------------------------------------------------------------
// モジュールの呼び出し
const express = require("express");
const app = express();
const request = require("request")
const line = require("@line/bot-sdk"); // Messaging APIのSDKをインポート

// -----------------------------------------------------------------------------
// パラメータ設定
const line_config = {
    channelAccessToken: process.env.LINE_ACCESS_TOKEN, // 環境変数からアクセストークンをセット
    channelSecret: process.env.LINE_CHANNEL_SECRET // 環境変数からChannel Secretをセット
};

// -----------------------------------------------------------------------------
// Webサーバー設定
app.listen(process.env.PORT || 3000);

app.use(express.static(__dirname + "/"));

const bot = new line.Client(line_config);

// -----------------------------------------------------------------------------
// ルーター設定
app.post('/webhook', line.middleware(line_config), (req, res, next) => {
  // 先行してLINE側にステータスコード200でレスポンスする。
  res.sendStatus(200);
  console.log(req.body);
});
```

### Herokuにデプロイ
#### gitの初期化
BOT本体をHerokuサーバーに載せて動かします。
Herokuはgitを使用するので、gitを使えるようにしましょう。
git initで初期化します。
作成場所は作成したディレクトリ(`sns-ojisan`)のルート直下です。

```
$ git init
```

`.gitignore`を作成し、追跡対象外のファイルを記述します。
作成場所は作成したディレクトリ(`sns-ojisan`)のルート直下です。

```.gitignore

npm-debug.log
node_modules
```

Heroku側の設定ファイル、Procfileを作成し、以下を記述します。
作成場所は作成したディレクトリ(`sns-ojisan`)のルート直下です。

```
web: node index.js
```

#### Herokuにログイン

Herokuにログインします。
場所は作成したディレクトリ(`sns-ojisan`)のルート直下です。

```
$ heroku login
Enter your Heroku credentials.
Email: あなたのEmail
Password (typing will be hidden): 
Logged in as あなたのEmail
```

#### アプリケーションの作成

```
$ heroku apps:create あなたのアプリ名
Creating ⬢ あなたのアプリ名... done
https://あなたのアプリ名.herokuapp.com/ | https://git.heroku.com/あなたのアプリ名.git
```

#### Messaging APIのSDKで必要となる環境変数のセット

こちらで使用する`あなたのChannel ID`や`あなたのChannel Secret`や`あなたのアクセストークン`は先ほどLINE Developersで作成したチャネルのページに記載があるので、そちらをコピペします。

```
$ heroku config:set LINE_CHANNEL_ID=あなたのChannel ID
$ heroku config:set LINE_CHANNEL_SECRET=あなたのChannel Secret
$ heroku config:set LINE_ACCESS_TOKEN=あなたのアクセストークン
```

ここまでできたら、これまでに作成したコードをレポジトリにコミットし、Herokuへデプロイします。

```
$ git add .
$ git commit -m "First commit"
$ git push -u heroku master
```

で、アプリケーションの作成は完了です！


### Webhookの設定

LINE Developersのチャネルページを開き、Webhook URLを以下のようにします。
`https://あなたのアプリ名.herokuapp.com/webhook`

Webhook URLをセット後、「接続確認」を押下し、「成功しました。」と出たら成功です。

### メッセージの返信
では早速特定のメッセージを送信したら、メッセージが返ってくるようにしましょう。
先ほどのコードの「ルーター設定」の中に追記をします。

```index.js

// -----------------------------------------------------------------------------
// モジュールの呼び出し
const express = require("express");
const app = express();
const request = require("request");
const line = require("@line/bot-sdk"); // Messaging APIのSDKをインポート

// -----------------------------------------------------------------------------
// パラメータ設定
const line_config = {
  channelAccessToken: process.env.LINE_ACCESS_TOKEN, // 環境変数からアクセストークンをセット
  channelSecret: process.env.LINE_CHANNEL_SECRET // 環境変数からChannel Secretをセット
};

// -----------------------------------------------------------------------------
// Webサーバー設定
app.listen(process.env.PORT || 3000);

app.use(express.static(__dirname + "/"));

const bot = new line.Client(line_config);

console.log("hoge");

// ルーター設定
app.post("/webhook", line.middleware(line_config), (req, res, next) => {
  // 先行してLINE側にステータスコード200でレスポンスする。
  res.sendStatus(200);
  // すべてのイベント処理のプロミスを格納する配列。
  let events_processed = [];
  // イベントオブジェクトを順次処理。
  req.body.events.forEach(event => {
    var options = {
      url: "https://api.line.me/v2/bot/profile/",
      headers: {
        Authorization: "Bearer {アクセストークン}"
      },
      json: true
    };
    request.get(options, (req, res, next) => {
      if (event.type == "message" && event.message.type == "text") {
        // ユーザーからのテキストメッセージが「こんにちは」だった場合のみ反応。
        if (event.message.text == "こんにちは") {
          // replyMessage()で返信し、そのプロミスをevents_processedに追加。
          events_processed.push(
            bot.replyMessage(event.replyToken, {
              type: "text",
              text: "どうもどうも"
            })
          );
        }
      }
    });
  });

  // すべてのイベント処理が終了したら何個のイベントが処理されたか出力。
  Promise.all(events_processed).then(response => {
    console.log(`${response.length} event(s) processed.`);
  });
});

```

![S__92749882.jpg](https://qiita-image-store.s3.amazonaws.com/0/146512/55a3754f-9a6d-94c3-5db3-03bef32a6664.jpeg)


#### おじさんっぽくする
よりおじさんに近づけるために、以下を行います。

* 文章を長くする
* 絵文字をつける
* 聞いてもない自分の話をする
* 誘う

では、「どうもどうも」となっている箇所に以下の文章に変更しましょう。


`コンニチハ😆🎵\n今日もお仕事頑張ってね‼️そういえば最近近所にできたカフェに行ってきたよ😄\n結構いい雰囲気だったから今度キミを連れて行きたいな〜なーんちゃって😅じゃあ今から元気にコーヒー淹れてきます☕️✨`

......うん。

<img width="320" alt="S__92749882.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/146512/5486f597-71fe-89cd-b5ef-f909398dcef8.jpeg">


### ユーザー名の取得
LINEの提供するMessaging APIを使用してユーザー名を取得します。
ユーザー名の取得には以下が必要になります。

* メッセージを送ったユーザーのユーザーIDの取得
* 取得したユーザーIDを使用して送信元のユーザーの設定をGETしに行く
* GETしたユーザー情報からユーザー名のみを抜き取り表示

まず、ユーザーIDを取得します。
イベントオブジェクトのリクエストの箇所を一部書き換えます。


```index.js
//省略

  req.body.events.forEach((event) => {
    let userId = event.source.userId; //追加：ユーザーIDを取得
    var options = {
      url: 'https://api.line.me/v2/bot/profile/' + userId, //追加：ユーザーのプロフィールにアクセス
      headers: {
        'Authorization': 'Bearer {アクセストークン}'
      },
      json: true
    }
    console.log(userId);
    request.get(options, (req, res, next) => {
      if (event.type == "message" && event.message.type == "text") {
        // ユーザーからのテキストメッセージが「こんにちは」だった場合のみ反応。
        if (event.message.text == 'こんにちは') {
          // replyMessage()で返信し、そのプロミスをevents_processedに追加。
          events_processed.push(bot.replyMessage(event.replyToken, {
            type: "text",
            text: "コンニチハ😆🎵\n今日もお仕事頑張ってね‼️そういえば最近近所にできたカフェに行ってきたよ😄\n結構いい雰囲気だったから今度キミを連れて行きたいな〜なーんちゃって😅じゃあ今から元気にコーヒー淹れてきます☕️✨"
          }));
        }
      }
    });
  });
```

#### よりおじさんに近づける
現状ではおじさんの特徴である「〜チャン」と呼んでくれません。
呼ばせるようにしましょう。

* 取得したユーザーIDを使用し、ユーザーIDからユーザー名を取得
上記の処理で、ユーザーIDが取得できたので、ユーザー名を取得できるようになります。
ついでに、「〜チャン」をつけるとよりおじさんチックにしてやりましょう。

ユーザー名はレスポンスの中のbody内のdisplayNameに入っているため、以下のような変数定義を追加し、
おじさんが返事してくれるテキストに変数名 + テキストで入力します。

```index.js

//省略
request.get(options, (req, res, next) => {
      let userName = res.body.displayName; //ユーザー名を変数に格納
      if (event.type == "message" && event.message.type == "text") {
        // ユーザーからのテキストメッセージが「こんにちは」だった場合のみ反応。
        if (event.message.text == 'こんにちは') {
          // replyMessage()で返信し、そのプロミスをevents_processedに追加。
          events_processed.push(bot.replyMessage(event.replyToken, {
            type: "text",
            text: userName + "チャンコンニチハ😆🎵\n今日もお仕事頑張ってね‼️そういえば最近近所にできたカフェに行ってきたよ😄\n結構いい雰囲気だったから今度" + userName + "チャンを連れて行きたいな〜なーんちゃって😅じゃあ今から元気にコーヒー淹れてきます☕️✨" //変数を使用して〜ちゃんを実現
        }));
      }
    }
  });
});
```

これで「キミ」とかが名前になったはず。。。
<img width="320" alt="S__92749880.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/146512/2166f8e1-d939-81a9-bacd-0a422e0d8796.jpeg">


#### おまけ
「こんにちは」だけではなく、いろんな返答をしてほしい場合は以下を参考にif文の条件を書き換える・追加等をしてください。

* event.message.text.match('hoge')
    * 部分一致したものに、反応してくれます。else ifを組み合わせることでいろんな言葉のバリエーションを増やす事ができます。

* event.message.text
    * テキストでメッセージがきた場合に反応するよう設定します。デフォルトの設定にもなるかも。

こんな感じでカオス・いい感じにおじさんぽくなります。
<img width="320" alt="S__92749881.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/146512/4ccf8214-7794-f979-bf58-4e6f895f5e8c.jpeg">


その他、スタンプを送信した場合や画像を送信した場合等は、
以下に公式のリファレンスがあるので、参考にしてみてください！

[公式ドキュメント](https://developers.line.biz/ja/docs/messaging-api/)


### ソースコード大公開

```index.js
// -----------------------------------------------------------------------------
// モジュールの呼び出し
const express = require("express");
const app = express();
const request = require("request")
const line = require("@line/bot-sdk"); // Messaging APIのSDKをインポート

// -----------------------------------------------------------------------------
// パラメータ設定
const line_config = {
  channelAccessToken: process.env.LINE_ACCESS_TOKEN, // 環境変数からアクセストークンをセット
  channelSecret: process.env.LINE_CHANNEL_SECRET // 環境変数からChannel Secretをセット
};

// -----------------------------------------------------------------------------
// Webサーバー設定
app.listen(process.env.PORT || 3000);

app.use(express.static(__dirname + "/"));

const bot = new line.Client(line_config);

// ルーター設定
app.post('/webhook', line.middleware(line_config), (req, res, next) => {
  // 先行してLINE側にステータスコード200でレスポンスする。
  res.sendStatus(200);
  // すべてのイベント処理のプロミスを格納する配列。
  let events_processed = [];
  // イベントオブジェクトを順次処理。
  req.body.events.forEach((event) => {
    let userId = event.source.userId;
    var options = {
      url: 'https://api.line.me/v2/bot/profile/' + userId,
      headers: {
        'Authorization': 'Bearer {アクセストークン}'
      },
      json: true
    }
    request.get(options, (req, res, next) => {
      let userName = res.body.displayName;
      if (event.type == "message" && event.message.type == "text") {
        // ユーザーからのテキストメッセージが「こんにちは」だった場合のみ反応。
        if (event.message.text == 'こんにちは') {
          // replyMessage()で返信し、そのプロミスをevents_processedに追加。
          events_processed.push(bot.replyMessage(event.replyToken, {
            type: "text",
            text: userName + "チャンコンニチハ😆🎵\n今日もお仕事頑張ってね‼️そういえば最近近所にできたカフェに行ってきたよ😄\n結構いい雰囲気だったから今度" + userName + "チャンを連れて行きたいな〜なーんちゃって😅じゃあ今から元気にコーヒー淹れてきます☕️✨"
          }));
        }
      }
    });
  });

  // すべてのイベント処理が終了したら何個のイベントが処理されたか出力。
  Promise.all(events_processed).then(
    (response) => {
      console.log(`${response.length} event(s) processed.`);
    }
  );
});
```

## おわりに

我ながら気持ち悪いもの作ったなぁ...引かれるよなぁ...とは思っているものの、後悔はしていません。
「需要がないじゃん」・「何が目的で作ったの？」と問われるとは思います。
目的は強いていうなら「どっかのAPI使ってデータのやりとりをやってみたかったから」です。

個人の考えではありますが、この手のモノは目的とか需要はどことかは作った後に考えたらいいのではないかと思っています。
理由はただ、そういうことを考えている間手が止まって、進むはずのものや得られるはずの知識・モチベーションが維持できなくなってしまうからです。
鉄は熱いうちに打てと言いますが、本当にそうだと思っていて
作りたい欲があるうちに作ることで、早い段階で自身のスキルアップにも繋がるんじゃないかなと。。。

ヘンテコなものや、しょうもないものが出来上がったとしても

「**いいじゃん！手動かしたんだから！！！！それより作ったことを褒めて！！！！！🎅**」

というスタンスでこのクリスマスは乗り切ろうと思います。

ではでは長くなりましたが、メリークリスマス！イブイブイブ！

※仕事等はちゃんと目的ありきで作ったりしているので常にそうしているわけではないです......！