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

LineメッセージをSlackへ転送させるスクリプト

More than 1 year has passed since last update.

LineメッセージをSlackへ転送させるスクリプト

主な機能

  • LineメッセージをSlackへ転送
  • スプレットシートへフォロユーザーのID、グループIDを追記
  • グループメッセージをSlackチャネルへ振り分け(スプレットシートに事前設定が必要)
  • 受信ファイルをGoggleDriveへアップロード

環境変数説明

環境変数名 説明 参考
CHANNEL_ACCESS_TOKEN Line Apiへのアクセストークン -
SLACK_LEGACY_TOKEN Slackのレガシートークン Slackのトークンについては こちら を参照
SPREADSHEET_KEY スプレットシートへのキー スプレットシートキーについては こちら を参照
SHEET_NAME アクセスするスプレットシートのシート名 -
SYSTEM_NOTIFICATION_CHANNEL 通知先として設定するSlackのチャンネル名 -
NOTIFICATION_CHANNEL 個別メッセージ、未設定グループメッセージの通知先 Slackチャンネル名 -
SYSTEM_ICON システムアナウンス時の表示アイコン(URL) -
LINE_UPLOAD_FORDER Lineで送られてきたファイルをアップロードするフォルダ名 -

実装コード

/** @type {string} line チャンネルアクセストークン */
var CHANNEL_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('CHANNEL_ACCESS_TOKEN');
/** @type {string} Slack レガシートークン */
var SLACK_LEGACY_TOKEN = PropertiesService.getScriptProperties().getProperty('SLACK_LEGACY_TOKEN');

/** @type {object} スプレットシートオブジェクト */
var SPREADSHEETS = SpreadsheetApp.openById(PropertiesService.getScriptProperties().getProperty('SPREADSHEET_KEY'));
/** @type {object} [description] */
var SHEET = SPREADSHEETS.getSheetByName(PropertiesService.getScriptProperties().getProperty('SHEET_NAME'));
/** @type {array} sheetデータが格納された配列 */
var SHEET_DATA = SHEET.getDataRange().getValues();


/**
 * Postを受け付けるメソッド
 * @method doPost
 * @param  {object} e イベントデータ
 * @return {Int}    戻り値(使用しないので0を返す)
 */
function doPost(e) {
  /** @type {string} メッセージの通知先 */
  var SYSTEM_NOTIFICATION_CHANNEL = PropertiesService.getScriptProperties().getProperty('SYSTEM_NOTIFICATION_CHANNEL');
  /** @type {string} メッセージの通知先 */
  var NOTIFICATION_CHANNEL = PropertiesService.getScriptProperties().getProperty('NOTIFICATION_CHANNEL');

  var line = JSON.parse(e.postData.contents).events[0];
  console.log(JSON.stringify(line));
  switch (line.type) {
    case 'follow':
      var profile = get_line_profile(line);
      idRecordsRegistration(line, profile, '', 1, 1);
      postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, '```フォローしました。\nステータスメッセージ: ' + profile.statusMessage + '```', profile);
      break;
    case 'unfollow':
      var rowNo = searchIdFromSpreadSheet(line.source.userId);
      if (rowNo == undefined) {
        postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, '```ブロックされましたが、該当IDが見つかりませんでした。\nID: ' + line.source.userId + '\n```');
      } else {
        SHEET.getRange("E"+(rowNo+1)).setValue(0);
        SHEET.getRange("F"+(rowNo+1)).setValue(0);
        var userName = getValFromCell(rowNo, 0);
        postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, '```ブロックされました。\nNAME: ' + userName + '\n```');
      }
      break;
    case 'join':
      idRecordsRegistration(line, undefined, '', 1, 0);
      postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, 'グループに追加されました。\n登録情報を確認してください。 ' + line.source.groupId);
      break;
    case 'leave':
      var groupInfo = getValFromCell(searchIdFromSpreadSheet(line.source.groupId), 0);
      if (groupInfo == '') {
        postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, 'グループから外されましたが、該当IDが見つかりませんでした。\n ' + line.source.groupId);
      } else {
        SHEET.getRange("E"+(rowNo+1)).setValue(0);
        SHEET.getRange("F"+(rowNo+1)).setValue(0);
        postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, 'グループから外されました。\n ' + groupInfo);
      }
      break;
    case 'message':
      var profile = get_line_profile(line);
      switch (line.source.type) {
        case 'user':
          postSlackMessage(NOTIFICATION_CHANNEL, get_line_message(line), profile);
          break;
        case 'group':
        case 'room':
          var groupInfo = getValFromCell(searchIdFromSpreadSheet(line.source.groupId), 0);
          var sendCh = getValFromCell(searchIdFromSpreadSheet(line.source.groupId), 3);
          if (groupInfo == '') {
            groupInfo = line.source.groupId;
          }
          if (sendCh == '') {
            sendCh = NOTIFICATION_CHANNEL;
          }
          postSlackMessage(sendCh, get_line_message(line), profile, groupInfo);
          break;
      }
      break;
    default:
      postSlackMessage(SYSTEM_NOTIFICATION_CHANNEL, line);
      break;
  }
  return 0;
}

/**
 * lineメッセージ内容毎の仕分け処理
 * @method get_line_message
 * @param  {object}     line line eventオブジェクト
 * @return {string}        メッセージ内容 or GDriveへアップロしたオブジェクトUrl
 */
function get_line_message(line) {
  switch (line.message.type) {
    case 'text':
      return line.message.text;
    case 'image':
      var blob = get_line_content(line.message.id);
      return uploadToGoogleDrive(line.message.id + '.png', blob, 'image/png');
    case 'video':
      var blob = get_line_content(line.message.id);
      return uploadToGoogleDrive(line.message.id + '.mp4', blob, 'video/mp4');
    case 'audio':
      var blob = get_line_content(line.message.id);
      return uploadToGoogleDrive(line.message.id + '.m4a', blob, 'audio/aac');
    case 'file':
      var blob = get_line_content(line.message.id);
      return uploadToGoogleDrive(line.message.fileName, blob);
    case 'location':
      return '位置情報を受信\naddress: ' + line.message.address + '\nlatitude: ' + line.message.latitude + '\nlongitude: ' + line.message.longitude;
    case 'sticker':
      var timestamp = new Date();
      return 'https://stickershop.line-scdn.net/stickershop/v1/sticker/' + line.message.stickerId + '/android/sticker.png?' + timestamp.getTime();
    default:
      return 0;
  }
}

/**
 * lineからコンテント取得処理
 * @method get_line_content
 * @param  {string}     message_id メッセージID
 * @return {object}          送信されたオブジェクト情報
 */
function get_line_content(message_id) {
  try {
    var headers = {
      'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN
    };
    var options = {
      'headers': headers
    };
    var url = 'https://api.line.me/v2/bot/message/' + message_id + '/content';
    var blob = UrlFetchApp.fetch(url, options);

    return blob;
  } catch (e) {

    console.log(e);
    return null;
  }
}

/**
 * lineからプロファイル情報取得
 * @method get_line_profile
 * @param  {object}     line line eventオブジェクト
 * @return {object}        プロファイルオブジェクト
 */
function get_line_profile(line) {
  var headers = {
    'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN
  };
  var options = {
    'headers': headers
  };
  var url;
  switch (line.source.type) {
    case 'user':
      url = 'https://api.line.me/v2/bot/profile/' + line.source.userId;
      break;
    case 'group':
      url = 'https://api.line.me/v2/bot/group/' + line.source.groupId + '/member/' + line.source.userId;
      break;
    case 'room':
      url = 'https://api.line.me/v2/bot/room/' + line.source.groupId + '/member/' + line.source.userId;
      break;
  }
  var response = UrlFetchApp.fetch(url, options);
  var content = JSON.parse(response.getContentText());
  return content;
}

/**
 * slackへのメッセージ送信処理
 * @method postSlackMessage
 * @param  {string}     sendCh    送信先チャネル名
 * @param  {string}     mes       メッセージ情報
 * @param  {object}     profile   プロファイルオブジェクト情報
 * @param  {object}     auxiliary 補足情報'(グループ名とか)'
 * @return {object}         なし
 */
function postSlackMessage(sendCh, mes, profile, auxiliary) {
  /** @type {string} システムアイコン */
  var SYSTEM_ICON = PropertiesService.getScriptProperties().getProperty('SYSTEM_ICON');

  var slackApp = SlackApp.create(SLACK_LEGACY_TOKEN);
  if (profile != undefined) {
    var options = {
      username: profile.displayName + (auxiliary != undefined ? ' from ' + auxiliary : ''),
      icon_url: profile.pictureUrl
    };
  } else {
    var options = {
      username: 'system announce',
      icon_url: SYSTEM_ICON
    };
  }

  console.log(JSON.stringify(options));
  slackApp.postMessage(sendCh, mes, options);
}

/**
 * G Driveからフォルダ名に一致するフォルダIDを取得する
 * @method getAllFolderID
 * @param  {string}     folderName フォルダ名
 * @return {string}          フォルダID
 */
function getAllFolderID(folderName) {
  var all_folders = DriveApp.getFolders();

  while (all_folders.hasNext()) {
    var folder = all_folders.next();
    if (folderName == folder.getName()) {
      return folder.getId();
    }
  }

  console.log('not unmatch folder name:' + folderName);
  return null;
}

/**
 * G Driveへファイルのアップロード処理
 * @method uploadToGoogleDrive
 * @param  {string}            fileName ファイル名
 * @param  {object}            blob     blobオブジェクト(ファイルオブジェクト)
 * @param  {string}            fileType ファイルMIMEタイプ
 * @return {string}                     ファイルへのアクセスURL
 */
function uploadToGoogleDrive(fileName, blob, fileType) {
  /** @type {string} g Drive上のアップロード先 */
  var LINE_UPLOAD_FORDER = PropertiesService.getScriptProperties().getProperty('LINE_UPLOAD_FORDER');

  var file = null;
  try {
    var folderId = getAllFolderID(LINE_UPLOAD_FORDER);
    if (folderId != null) {
      var fileOption = {
        title: fileName,
        parents: [{
          id: folderId
        }]
      };
      if (fileType != undefined) fileOption.mimeType = fileType


      file = Drive.Files.insert(fileOption, blob);
      return file.thumbnailLink?file.thumbnailLink:file.embedLink;

    } else {
      file = DriveApp.createFile(blob);
      return file.getThumbnail()!=null?file.getThumbnail():file.getUrl();
    }
  } catch (e) {
    return e.message;
  }
}

/**
 * ID情報の登録処理(Spread sheetへの書き込み)
 * @method idRecordsRegistration
 * @param  {object}              line       lineメッセージオブジェクト
 * @param  {object}              profile    プロファイル情報 無い場合はundefined
 * @param  {string}              sendCh     送信するチャンネル
 * @param  {integer}             followFlg  フォローされているか判定情報
 * @param  {integer}             enabledFlg 有効無効フラグ
 * @return {none}                           なし
 */
function idRecordsRegistration(line, profile, sendCh, followFlg, enabledFlg) {
    var id = '';
    var name = '';

    switch (line.source.type) {
      case 'user':
        id = line.source.userId;
        break;
      case 'group':
      case 'room':
        id = line.source.groupId;
        break;
      default:
        console.log('未定義type');
        return ;
    }

    if (profile != undefined) {
        name = profile.displayName
    }

    SHEET.appendRow([name, id, line.source.type, sendCh, followFlg, enabledFlg]);
}

/**
 * スプレットシートデータから指定値が格納された行番号を返す
 * @method searchIdFromSpreadSheet
 * @param  {string}               val 検索値
 * @return {stirng}                   該当値が格納された行番号 無い場合はundefined
 */
function searchIdFromSpreadSheet(val) {
    for (var rowNo=0; rowNo<SHEET_DATA.length; ++rowNo) {
      if (SHEET_DATA[rowNo].indexOf(val) !== -1) {
        return rowNo;
      }
    }

    return undefined;
}

/**
 * スプレットシートデータの特定セルからデータを取り出すあ
 * @method getValFromCell
 * @param  {integer}      row 行番号
 * @param  {integer}      col 列番号
 * @return {string}           セルに格納されている情報
 */
function getValFromCell(row, col) {
  if (row == undefined || col == undefined) {
    return '';
  }

  return SHEET_DATA[row][col];
}

参考(Special Thanks)

Why not register and get more from Qiita?
  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