電算倶楽部 富山県のコンピュータ社会人サークル

富山県、特に滑川市、富山市、魚津市周辺で活動している社会人サークルです。

SlackとLINEを連携する(3) Slack→LINE

前回はLINEからSlackへ通知できるようにしたので、今回はSlackからLINEへ通知できるようにします。

新しくGoogle Apps Script(以下GAS)ファイルを作成してプログラムを作っていきます。

長いので要点だけ解説します。

ソースコード

/**
 * @fileoverview Slackのgeneralチャンネルに通知されたメッセージをLINEボットに転送する。
 * @author smp
 */

// LINEトークン
var LINE_TOKEN = "(秘密)";
// LINEチャンネルアクセストークン
var LINE_ACCESS_TOKEN = "(秘密)";
// LINEメッセージプッシュ用APIのURL
var LINE_MESSAGE_PUSH_URL = "https://api.line.me/v2/bot/message/push";
// SLACKトークン
var SLACK_TOKEN = '(秘密)';
// GoogleドライブディレクトリID
var GOOGLE_DRIVE_DIR_ID = '(秘密)'
// LINEユーザ一覧ファイル名
var LINE_USER_LIST_FILENAME = 'LineUserList.json';

/**
 * @summary Postリクエスト処理。
 * Slack Outgoing WebhookからPOSTで通知された場合に実行する関数。
 * イベントに含まれるチャンネル名、ユーザ名、本文をLINEに通知する。
 * @param e Slackから通知されるメッセージ
 */
function doPost(e) {

  // 1.各種情報を取得
  // (1)チャンネル名
  var chName = e.parameter.channel_name;
  // (2)ユーザ名
  var userName = e.parameter.user_name;
  // (3)本文
  var text = e.parameter.text;
  // (4)Slackトークン
  var slackToken = e.parameter.token;

  // 2.メッセージの組み立て
  var msg = 'Slack投稿通知\n' +
    'チャンネル : ' + chName + '\n' +
    '発言者 : ' + userName + '\n' +
    '内容 : ' + text;

  // 3.送信
  switch (slackToken) {
    case SLACK_TOKEN:
      pushToLine(msg);
      break;
    default:
      // do nothing
  }
}

/**
 * @name LINEへメッセージ送信
 * @summary LINEユーザ一覧ファイルにある宛先へメッセージを送信する。
 * @param {string} text 本文
 */
function pushToLine(text) {
  // LINEユーザ一覧を読み込む。
  var lineUserList = getLineUserIdList();

  // 全LINEユーザに対して繰り返し処理を実行する。
  for (i in lineUserList) {
    var lineUserId = lineUserList[i];
    try {
      pushToLineOneUser(text, lineUserId);
    } catch (err) {
      console.log(err);
    }
  }
}

/**
 * @name LINEへメッセージ送信(1ユーザ)
 * @summary 指定したメッセージを、指定したLINEユーザID宛に送信する。
 * @param {string} text 本文
 * @param {string} lineUserId LINEユーザID
 */
function pushToLineOneUser(text, lineUserId) {

  // ヘッダデータ作成。
  var headers = {
    'Content-Type': 'application/json; charset=UTF-8',
    'Authorization': 'Bearer ' + LINE_ACCESS_TOKEN,
  };

  // POSTリクエストデータ作成。
  var postData = {
    'to': lineUserId,
    'messages': [
      {
        'type': 'text',
        'text': text,
      }
    ]
  };

  // 送信データを作成。
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': JSON.stringify(postData)
  };

  var response = UrlFetchApp.fetch(LINE_MESSAGE_PUSH_URL, options);
  return response;
}
/**
 * @name LINEユーザID一覧取得
 * @summary LINEユーザ一覧ファイルからLINEユーザIDの一覧を取得する。
 * @returns LINEユーザリスト
 */
function getLineUserIdList() {
  var jsonData = readJson(LINE_USER_LIST_FILENAME);
  var userIdList = [];
  // ユーザ一覧作成
  if (jsonData.list) {
    for (var i in jsonData.list) {
      userIdList.push(jsonData.list[i].userId);
    }
  }
  return userIdList;
}


/**
 * @name Google Drive内ファイル取得
 * @summary Google Driveのディレクトリパスとファイル名から、ファイルを取得する。
 * 存在しない場合は新しく作成する。
 * @param {string} dirId ディレクトリID
 * @param {string} fileName ファイル名
 * @returns ファイルオブジェクト
 */
function getGoogleDriveFile(dirId, fileName) {
  var dir = DriveApp.getFolderById(dirId);
  var file = dir.getFilesByName(fileName);
  if (file.hasNext()) {
    // 存在する場合
    return file.next();
  } else {
    // 存在しない場合
    return dir.createFile(fileName, '');
  }
}



/**
 * @name jsonファイル読み込み
 * @summary jsonファイルをGoogle Driveから読み込む。
 * jsonファイルを読み込み、パースして返す。
 * jsonファイルがない場合、新しくjsonファイルを作成する。
 * @param {string} jsonPath Google Drive内jsonファイルパス
 * @returns {object} 読み込んだjsonデータ
 */
function readJson(jsonPath) {
  var file = getGoogleDriveFile(GOOGLE_DRIVE_DIR_ID, jsonPath);
  var content = file.getBlob().getDataAsString();
  if (!content) {
    // ファイルの内容が空の場合
    return {};
  } else {
    var json = JSON.parse(content);
    return json;
  }
}

Postリクエスト受信と制御

今回もメッセージ受信にはdoPost関数を使用します。 ただし、引数eにはLINEからのメッセージではなくSlackからのメッセージが格納されています。

まずはeからLINEに通知したい各種情報を取得します。 今回はメッセージ用にチャンネル名、ユーザ名、本文を取得しました。 また、送信元が正しいか確認するためにSlackトークンも取得しておきます。

function doPost(e) {

  // 1.各種情報を取得
  // (1)チャンネル名
  var chName = e.parameter.channel_name;
  // (2)ユーザ名
  var userName = e.parameter.user_name;
  // (3)本文
  var text = e.parameter.text;
  // (4)Slackトークン
  var slackToken = e.parameter.token;

  // 2.メッセージの組み立て
  var msg = 'Slack投稿通知\n' +
    'チャンネル : ' + chName + '\n' +
    '発言者 : ' + userName + '\n' +
    '内容 : ' + text;

  // 3.送信
  switch (slackToken) {
    case SLACK_TOKEN:
      pushToLine(msg);
      break;
    default:
      // do nothing
  }
}

LINEへ通知

LINEへの通知データの中にはLINEのユーザIDが必要です。 そのため、getLineUserIdList関数を作りGoogle DriveのLineUserList.jsonから情報を取得します。

取得したすべてのLINE IDに対しループさせ、メッセージを送ります。

function pushToLine(text) {
  // LINEユーザ一覧を読み込む。
  var lineUserList = getLineUserIdList();

  // 全LINEユーザに対して繰り返し処理を実行する。
  for (i in lineUserList) {
    var lineUserId = lineUserList[i];
    try {
      pushToLineOneUser(text, lineUserId);
    } catch (err) {
      console.log(err);
    }
  }
}

確認

ウェブアプリケーションとして導入し、Slackのgeneralチャンネルにメッセージを投稿するとLINEに届きました。

課題

これで一通りSlackとLINEのメッセージのやり取りができるようになりました。しかしまだ以下のような課題があります。

  • ログをファイルとして保存しておきたい。画面からの確認はタイムラグがある(?)し、ちょっと使いづらい。
  • LINEで同時にブロック・ブロック解除すると、LineUserList.jsonに反映されない場合がある(競合している)。

対処したらまた記事にします。