Help us understand the problem. What is going on with this article?

LINE Botの友達情報をkintoneへ登録してみた

はじめに

LINE Botのプッシュメッセージ※を利用するためには 「userID」 が必要です。

userIDはアカウントごとに一意の値であり、
1つのBot(正確には1つのプロバイダー)から見たら、同じアカウントは同じuserIDとなります。
-> このuserIDでアカウントを特定することができる!!
-> -> 「Bot A にメッセージを送信すると、別のBot Bから返事が返ってくる」ってこともできます!!

しかし、
このuserIDは「友達登録時」「ユーザーからのメッセージ受信時」といったように、ユーザーが何かアクションをしないと取得できません :bomb:

ということで、
任意のタイミングでメッセージを送るためには、あらかじめuserIDをためておくDBが必要になります!


(ってことで、)


今回は友達登録された際に kintone へuserIDを登録する方法を書きます!!

※プッシュメッセージについては こちら をご覧ください

仕組み・フロー

ユーザーの操作が「友達登録」なのか「メッセージ送信」なのかは Webhookで受け取れるイベントの中にあるtypeで判断することができます。

event.body.events[].type

このtypeは、

  • follow -> 友達登録 (とブロック解除)
  • unfollow -> ブロック
  • message -> メッセージ送信

という感じに判断できます。なのであとはいい感じに分岐すれば良いだけです!
flow.png

ちょっとまじめに流れを書いてみると、

  1. Webhookを受け取る
  2. イベントオブジェクトからtypeとuserIDを取得する
  3. typeがfollowunfollowなら、userIDをクエリとしてkintoneのレコードを取得する
  4. followかつ該当レコードがなければ、userIDをkintoneへ登録する
    (ついでにアカウント名なども一緒に)
  5. followかつ該当レコードがあれば、「ブロック解除」の処理をする
    -> 取得したレコードを更新する
  6. unfollowであれば、該当レコードを「ブロック中」と更新する

こんな感じかと。(4へは最初の1回だけ。あとは5or6への処理を繰り返す)

kintone側の設定

DBとしてkintoneを使います!!! (S3やDynamoでもいいじゃんは :ng: ですw)

フィールド

kintoneのフィールドは以下のようにしました

フィールド フィールドコード 用途
作成日時 create_time Botを追加した日時
文字列(1行) line_name LINEのアカウント名
文字列(1行) line_id LINEのuserID
チェックボックス line_block Botのブロック判断

フィールドを配置するとこんな感じです。

スクリーンショット 2019-03-20 20.14.46.png

APIトークン

設定 > APIトークン から 閲覧/追加/編集にそれぞれチェックをつけてトークンを発行してください。

コード

GitHub においてあります。cloneすればとりあえず動くとは思います。

// 作業用ディレクトリへ移動
$ cd work
// リポジトリのクローン
$ git clone https://github.com/RyBB/LINE_FriendsRegist_kintone.git

// ディレクトリの移動
$ cd LINE_FriendsRegist_kintone
// node_moduleのインストール
$ npm install

中間サーバとしてLambda(とAPI Gateway)を利用しています。AWS側の細かい設定は省略!
(AWSの設定はこちらを参考にすると良いです)

index.js
const kintone = require('kintone-nodejs-sdk');
const line = require('@line/bot-sdk');

const kin_common = {
  url: process.env.KINTONE_DOMAIN,
  apiToken: process.env.KINTONE_APITOKEN,
  appId: process.env.KINTONE_APPID,
  fCode1: 'line_name',
  fCode2: 'line_id',
  fCode3: 'line_block',
};

const kintoneAuth = new kintone.Auth();
kintoneAuth.setApiToken(kin_common.apiToken);
const kintoneConnection = new kintone.Connection(kin_common.url, kintoneAuth);
const kintoneRecord = new kintone.Record(kintoneConnection);

// kintoneのレコードをクエリで条件取得する処理
const getRecords = (LINE_USER_ID) => {
  const query = kin_common.fCode2 + ' = "' + LINE_USER_ID + '"';
  return kintoneRecord.getRecords(kin_common.appId, query, ['$id'], true);
};

// kintoneのレコードを1件登録する処理
const postRecord = (LINE_USER_ID, LINE_NAME) => {
  const params = {
    [kin_common.fCode1]: {
      'value': LINE_NAME
    },
    [kin_common.fCode2]: {
      'value': LINE_USER_ID
    },
  };
  return kintoneRecord.addRecord(kin_common.appId, params);
};

// kintoneのレコードを1件更新する処理
const putRecord = (LINE_USER_ID, recordid, follow) => {
  const params = {
    [kin_common.fCode2]: {
      'value': LINE_USER_ID
    },
    [kin_common.fCode3]: {
      'value': follow === 'follow' ? [] : ['ブロック中']
    },
  };
  return kintoneRecord.updateRecordById(kin_common.appId, recordid, params);
};

exports.handler = async (event) => {
  const res = JSON.parse(event.body);
  const line_userId = res.events[0].source.userId;

  const client = new line.Client({
    channelAccessToken: process.env.LINE_ACCESS_TOKEN
  });

  let resp, line_resp;
  switch(res.events[0].type) { // メッセージのタイプを判断
    case 'follow': {
      resp = await getRecords(line_userId);
      if (resp.totalCount !== '0') {
        console.log('ブロック解除された');
        await putRecord(line_userId, resp.records[0].$id.value, 'follow');
        break;
      }
      console.log('新規登録された');
      line_resp = await client.getProfile(line_userId); // LINEのアカウント名を取得する
      await postRecord(line_resp.userId, line_resp.displayName);
      break;
    }
    case 'unfollow': {
      resp = await getRecords(line_userId);
      if (resp.totalCount !== '0') {
        console.log('ブロックされた');
        await putRecord(line_userId, resp.records[0].$id.value, 'unfollow');
      }
      break;
    }
    default: {
      // それ以外。何もしない
    }
  }
};

Lambda上の環境変数はこんなかんじ。
setting.png

ちょっと解説

コードをちょっとだけ解説します!

  • kintone Node.js SDK

今回はkintoneへのリクエストが多い (GET/POST/PUT) ので、SDKを利用しました。
最初にコネクションを設定しないといけないですが、それ以外はかなり楽に記述できます (メソッドとか)

詳しい書き方は こちら

kintone-nodejs-sdk_connection
// コネクション
const kintoneAuth = new kintone.Auth();
kintoneAuth.setApiToken(kin_common.apiToken);
const kintoneConnection = new kintone.Connection(kin_common.url, kintoneAuth);
const kintoneRecord = new kintone.Record(kintoneConnection);

// メソッド (GET/POST/PUT それぞれあります)
kintoneRecord.XXXX();
  • typeによる分岐

typeがfollowunfollowそれ以外かで分岐させています。

switch
  switch(res.events[0].type) { // メッセージのタイプを判断
    case 'follow': {
      resp = await getRecords(line_userId);
      if (resp.totalCount !== '0') {
        console.log('ブロック解除された');
        await putRecord(line_userId, resp.records[0].$id.value, 'follow');
        break;
      }
      console.log('新規登録された');
      line_resp = await client.getProfile(line_userId); // LINEのアカウント名を取得する
      await postRecord(line_resp.userId, line_resp.displayName);
      break;
    }
    case 'unfollow': {
      resp = await getRecords(line_userId);
      if (resp.totalCount !== '0') {
        console.log('ブロックされた');
        await putRecord(line_userId, resp.records[0].$id.value, 'unfollow');
      }
      break;
    }
    default: {
      // それ以外。何もしない
    }
  }
  • kintoneのレコード取得

kintoneのuserIDを格納するフィールド(line_id)と友達登録時(ブロック操作時)に受け取ったLINEのuserIDがマッチするレコードのみ取得するようにします。
もしマッチするレコードがあれば、すでにそのアカウントはkintoneへ登録済みってことになります!

kintone_getRecords
const getRecords = (LINE_USER_ID) => {
  const query = kin_common.fCode2 + ' = "' + LINE_USER_ID + '"';
  return kintoneRecord.getRecords(kin_common.appId, query, ['$id'], true);
};
  • kintoneのレコード更新

typeがfollowかつレコードが存在すれば、ブロック解除したことになるので、
kintoneのチェックボックスを空にします。
逆に、typeがunfollowであればチェックボックスに「ブロック中」と入れます。

kintone_putRecord
const putRecord = (LINE_USER_ID, recordid, follow) => {
  const params = {
    [kin_common.fCode2]: {
      'value': LINE_USER_ID
    },
    [kin_common.fCode2]: {
      'value': follow === 'follow' ? [] : ['ブロック中']
    }
  };
  return kintoneRecord.updateRecordById(kin_common.appId, recordid, params);
};

完成イメージ

こんな感じでkintoneにuserIDとアカウント名とブロック状況が登録されます。
line-kin.png

おわりに

あとは任意のタイミングでkintoneからuserIDを取得してメッセージを送るだけです。
アカウント名もclient.getProfile(line_userId);で取得しているのでデータとして扱いやすいかと!!

このくらいのDB管理であれば、むしろkintoneのほうがフォーム作ったりが簡単なのでおすすめです!
(きちんと運用する場合は「ブロックされたらレコードを削除する」などの処理のほうが良いですよ〜)

それでは!≧(+・` ཀ・´)≦

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away