LoginSignup
15
4

More than 1 year has passed since last update.

3タップで完了するLINEフォトアルバム #LINE #GAS #GoogleSpreadSheet

Last updated at Posted at 2021-12-18

スマホの中の写真 埋もれてない?

スマホがこの世に誕生し、撮られる写真はそれ以前の10倍に。
世界で1年に撮影される写真は瞬時に1兆枚を突破。
1スマホの中に少なくとも1,000枚以上の写真があるそうです。

③.PNG

せっかく撮影しても、整理できずにいませんか?。
「子供と仕事の写真ぐちゃぐちゃ・・」「あの思い出はどこ・・?」

思い出が埋もれたり、写真と一緒に消えてしまうのは悲しい。
とはいえ整理はめんどくさい。よくあるフォトアプリも挫折した。

そんな方々、そして誰より私たち夫婦の悩みを解決するために
日本一簡単操作で作れるフォトアルバムを作ります!

3タップフォトアルバムの試作品

できたものはこちら

①写真ボタンを押す ②写真を選ぶ ③送信

なんと3タップでアルバム投稿は完成!
入力せずとも、「投稿者」と「投稿日時」も自動で記入!
いつの、誰のお気に入り写真か、一瞬で振り返ることができます!

「日本一簡単」と謳うからには、私たちが挫折した
「意識高い系フォトアルバム」よりも楽に投稿できなければなりません。
実際に比較して、 「日本一楽に投稿」の基準を掲げました。

3タップで写真投稿 6工程でコメント投稿

「6工程でのコメント投稿」はまだ実現できていませんが、
今後絶対制作してみせます。(頑張る💪)

⑦.PNG

全体の流れ・コードはこちら

⑥.PNG

// 応答メッセージURL
const REPLY = "https://api.line.me/v2/bot/message/reply";

// アクセストークン
const ACCESS_TOKEN = "********";

// スプレッドシート情報

const SHEET_ID   = '********';
const SHEET      = SpreadsheetApp.openById(SHEET_ID).getSheetByName('シート1');

// Google Drive ID
const GOOGLE_DRIVE_ID = "1WZOXuCbs8yb574GHQLp5Ov6SJYPKFLTz";

// LINEから送られてきたデータを取得 doPost()
function doPost(e) {

  //メッセージ受信
  const data = JSON.parse(e.postData.contents).events[0];
  //ユーザーID取得
  const lineUserId = data.source.userId;
  // リプレイトークン取得
  const replyToken = data.replyToken;
  // 送信されたメッセージの種類を取得
  // https://developers.line.biz/ja/docs/messaging-api/message-types/#sticker-messages
  const postType = data.message.type;

  // 写真の時
  if ( "image" === postType ) {
    imageSave(replyToken, lineUserId, data);
  } else {
    sendMessage(replyToken, '画像を送信してください')
  }

}

// 送信された画像を保存 imageSave()
function imageSave(replyToken, lineUserId, data) {

  // LINEから画像取得 getImg()
  const imgData = getImg(data);

  // Googleドライブに保存 saveImg()
  const imgInfo = saveImg(imgData, lineUserId);

  // //「保存完了」とLINEにメッセージを送る
  sendMessage(replyToken, imgInfo);

}

// LINEから画像取得 getImg()
function getImg(data) {

  const IMG_URL = 'https://api-data.line.me/v2/bot/message/' + data.message.id + '/content';
  const HEAD = {
    "method":"get",
    "headers": {
      "Authorization" : "Bearer " + ACCESS_TOKEN
    }
  }
  const imgData = UrlFetchApp.fetch(IMG_URL, HEAD);
  return imgData;

}

// Googleドライブに保存 saveImg()
function saveImg(imgBinary, lineUserId){

  //GoogleDriveフォルダID
  const folder = DriveApp.getFolderById(GOOGLE_DRIVE_ID);
  //ランダムな文字列を生成して、画像のファイル名とする
  const fileName = Math.random().toString(36).slice(-8);
  //Googleドライブのフォルダに画像を生成
  const imageFile = folder.createFile(imgBinary.getBlob().setName(fileName));
  //「保存しました」としたメッセージを変数に代入
  const imgInfo = '保存したよ!https://docs.google.com/spreadsheets/d/1X8sR_aCzZydKSdAInkBZG9TcDeWInoq38TeTKRt9-KA/edit#gid=0';

  //画像ファイルURL取得
  const imageURL = '****スプシURL****' + imageFile.getId();

  //画像ファイルにリンクでアクセスの権限付与
  imageFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);

  debugImgLog(imageURL, lineUserId);
  return imgInfo;

}

// スプレッドシートに画像を保存 debugImgLog()
function debugImgLog(text, userId) {

  if ('' == text) {
    return;
  }

  const date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss')
  const userName = getUserDisplayName(userId)
  const userImg = getUserDisplayImg(userId)

  SHEET.appendRow([userId, userImg, userName, text, date,'=image("'+text+'")']);
  // 日付順に並び替え
  const numColumn = SHEET.getLastColumn(); // 最後列の列番号を取得
  const numRow    = SHEET.getLastRow()-1;  // 最後行の行番号を取得
  const dataRange = SHEET.getRange(2, 1, numRow, numColumn);
  dataRange.sort([{column: 5, ascending: false}]);

}

// ユーザーのプロフィール名取得 getUserDisplayName()
function getUserDisplayName(userId) {

  const url = 'https://api.line.me/v2/bot/profile/' + userId;
  const userProfile = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' : 'Bearer ' + ACCESS_TOKEN,
    },
  })
  return JSON.parse(userProfile).displayName;

}

// ユーザーのプロフィール画像取得 getUserDisplayImg()
function getUserDisplayImg(userId) {

  const url = 'https://api.line.me/v2/bot/profile/' + userId;
  const userProfile = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' :  'Bearer ' + ACCESS_TOKEN,
    },
  })
  return JSON.parse(userProfile).pictureUrl;

}

// LINEにメッセージ送信 sendMessage()
function sendMessage(replyToken, replyText) {

  const postData = {
    "replyToken" : replyToken,
    "messages" : [
      {
        "type" : "text",
        "text" : replyText
      }
    ]
  };

  const headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    "Authorization" : "Bearer " + ACCESS_TOKEN
  };

  const options = {
    "method" : "POST",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };

  return UrlFetchApp.fetch(REPLY, options);

参考記事(ありがたい😢) LINEで送った画像をGoogleドライブに保存するメモ

詰まっているところ コメントを入れられない

 写真+コメントも「思い出」として残したい。そこで
function doPost(e)の中身に下記を追加し、
if文を下記のように変更することで実装を試みました。

いまのところコメントは指定セルに入るのですが、画像が保存されなくなってしまいます。
もし原因・対策分かる方、よければ教えてください。。。

function doPost(e)の中に追記したコード

 const json = JSON.parse(e.postData.contents);
  const message = json.events[0].message.text; 
  const messageParameter = message.split(/\r\n|\n/);
  const lastRow = SHEET.getLastRow();

書き変えたif文と意図 コメントは入るが画像が入らない

  // 写真の時はデータ取得
  if ( "image" === postType ) {
    imageSave(replyToken, lineUserId, data);

   //写真でなければG2のセルに入力

  } else {
      var lastRow = SHEET.getLastRow();
  SHEET.getRange('G' +  2).setValue(messageParameter[0]);
  }

うまくいっていない全体コードはこちらです (クリックで表示)



// 応答メッセージURL
const REPLY = "https://api.line.me/v2/bot/message/reply";

// アクセストークン
const ACCESS_TOKEN = "********";

// スプレッドシート情報

const SHEET_ID   = '********';
const SHEET      = SpreadsheetApp.openById(SHEET_ID).getSheetByName('シート1');

// Google Drive ID
const GOOGLE_DRIVE_ID = "********";

// LINEから送られてきたデータを取得 doPost()
function doPost(e) {

  //メッセージ受信
  const data = JSON.parse(e.postData.contents).events[0];
  //ユーザーID取得
  const lineUserId = data.source.userId;
  // リプレイトークン取得
  const replyToken = data.replyToken;
  // 送信されたメッセージの種類を取得
  // https://developers.line.biz/ja/docs/messaging-api/message-types/#sticker-messages
  const postType = data.message.type;

  //コメント入力のために追加しているか箇所①
  const json = JSON.parse(e.postData.contents);
  const message = json.events[0].message.text; 
  const messageParameter = message.split(/\r\n|\n/);
  var lastRow = SHEET.getLastRow();
  //追加①ここまで

  // if文書き変え 写真の時データ取得
  if ( "image" === postType ) {
    imageSave(replyToken, lineUserId, data);

   //そうじゃない時はG2に文字入力

  } else {
      var lastRow = SHEET.getLastRow();
  SHEET.getRange('G' +  2).setValue(messageParameter[0]);
  }

  //書き変えここまで

}




// 送信された画像を保存 imageSave()
function imageSave(replyToken, lineUserId, data) {

  // LINEから画像取得 getImg()
  const imgData = getImg(data);

  // Googleドライブに保存 saveImg()
  const imgInfo = saveImg(imgData, lineUserId);

  // //「保存完了」とLINEにメッセージを送る
  sendMessage(replyToken, imgInfo);

}

// LINEから画像取得 getImg()
function getImg(data) {

  const IMG_URL = 'https://api-data.line.me/v2/bot/message/' + data.message.id + '/content';
  const HEAD = {
    "method":"get",
    "headers": {
      "Authorization" : "Bearer " + ACCESS_TOKEN
    }
  }
  const imgData = UrlFetchApp.fetch(IMG_URL, HEAD);
  return imgData;

}

// Googleドライブに保存 saveImg()
function saveImg(imgBinary, lineUserId){

  //GoogleDriveフォルダID
  const folder = DriveApp.getFolderById(GOOGLE_DRIVE_ID);
  //ランダムな文字列を生成して、画像のファイル名とする
  const fileName = Math.random().toString(36).slice(-8);
  //Googleドライブのフォルダに画像を生成
  const imageFile = folder.createFile(imgBinary.getBlob().setName(fileName));
  //「保存しました」としたメッセージを変数に代入
  const imgInfo = '保存したよ!https://docs.google.com/spreadsheets/d/1X8sR_aCzZydKSdAInkBZG9TcDeWInoq38TeTKRt9-KA/edit#gid=0';

  //画像ファイルURL取得
  const imageURL = 'https://drive.google.com/uc?export=view&id=' + imageFile.getId();

  //画像ファイルにリンクでアクセスの権限付与
  imageFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);

  debugImgLog(imageURL, lineUserId);
  return imgInfo;

}

// スプレッドシートに画像を保存 debugImgLog()
function debugImgLog(text, userId) {

  if ('' == text) {
    return;
  }

  const date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss')
  const userName = getUserDisplayName(userId)
  const userImg = getUserDisplayImg(userId)

  SHEET.appendRow([userId, userImg, userName, text, date,'=image("'+text+'")']);
  // 日付順に並び替え
  const numColumn = SHEET.getLastColumn(); // 最後列の列番号を取得
  const numRow    = SHEET.getLastRow()-1;  // 最後行の行番号を取得
  const dataRange = SHEET.getRange(2, 1, numRow, numColumn);
  dataRange.sort([{column: 5, ascending: false}]);

}

// ユーザーのプロフィール名取得 getUserDisplayName()
function getUserDisplayName(userId) {

  const url = 'https://api.line.me/v2/bot/profile/' + userId;
  const userProfile = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' : 'Bearer ' + ACCESS_TOKEN,
    },
  })
  return JSON.parse(userProfile).displayName;

}

// ユーザーのプロフィール画像取得 getUserDisplayImg()
function getUserDisplayImg(userId) {

  const url = 'https://api.line.me/v2/bot/profile/' + userId;
  const userProfile = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' :  'Bearer ' + ACCESS_TOKEN,
    },
  })
  return JSON.parse(userProfile).pictureUrl;

}

// LINEにメッセージ送信 sendMessage()
function sendMessage(replyToken, replyText) {

  const postData = {
    "replyToken" : replyToken,
    "messages" : [
      {
        "type" : "text",
        "text" : replyText
      }
    ]
  };

  const headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    "Authorization" : "Bearer " + ACCESS_TOKEN
  };

  const options = {
    "method" : "POST",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };

  return UrlFetchApp.fetch(REPLY, options);

}

工夫した点と目指したいこと

送られた写真をそのままスプレッドシートに張り付けたかったのですが断念。
そこで一旦GoogleDriveに写真を保存し、Drive内写真のURLをシートに入力させ、
=image("写真のURL")とセル内で表示させることで、画像表示させました。

⑩.PNG

アルバムっぽく表示させるため、別シートを設定、
LINEから飛んできた情報を一旦原紙シートに記載し、それとは別のシートで、
自動的にアルバムのように表示させることにしました。

⑪.PNG

とはいえまだ手作りExcel感満載ですので、今後はwebアプリとして表現できるように
実装を進めたいと思っています。

まだまだ雑ですがこのような形に仕上げていきたいです。

思い出が消えない世界を作りたい

 将来「写真分類・投稿・アルバム化」も全部「自動」にして
 あとは振り返るだけ!そんな「思い出が消えない世界」を作りたいと思っています。

 「日本一簡単なLINEフォトアルバム」は、その夢へのはじめの一歩です。

 夢を叶えるために、1月中旬には人生初のクラウドファンディングにも挑戦しますので、
 これからも応援してくださるととても嬉しいです!!

@shiracamus さん編集リクエストありがとうございました!!#見落としていたのでありがたいです!!

⑫.PNG
⑬.PNG

15
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
4