gasとラインbotで近場のgotoeat対象店を返してくれるbotを作成してみました

##概要
ラインから位置情報を送付した場合、スプレッドシートに格納されたgotoeat対象店を一つラインへ返答するもの

追加は下記のリンクから可能です！よろしくお願いいたします。
リッチメニューを押して位置情報を送信したら30秒程で返ってきます、今は正直かなり処理が重いので早くできるようにしたい
ちなみに現在は広島と新潟しか対応していないです、今後データセット増やしていく予定ではありますが
https://lin.ee/blCBUNL

##概要の詳細
単純な仕組みでスプレッドシートに入っているお店の緯度と経度の合計値とラインから送られた位置情報の経度と緯度の合計値を引き算し、その値が限りなく0に近いものを返答するというもの
→ラインの位置情報送信はurlスキームの↓こちらを採用
https://line.me/R/nv/location/

##作成の際のワークフロー
1.pythonで県別のgotoeat対象店の飲食店のデータをスクレイピングします
→こちらのスクレイピングのコードはもしご要望が多ければ公開します

2.gasでコード生成
→個人的に詰まったのは近似値の保持(gasはデータの保持はあまり向いていない)

##メインのソースコード

```javascript:コード.js
//notactive
const CHANNEL_ACCESS_TOKEN = "アクセストークン";
const line_endpoint = 'https://api.line.me/v2/bot/message/reply';
//LINE Messaging APIからPOST送信を受けたときに起動する
// e はJSON文字列
function doPost(e){
  if (typeof e === "undefined"){
    //eがundefinedの場合動作を終了する
    return;
  } 

  //JSON文字列をパース(解析)し、変数jsonに格納する
  var json = JSON.parse(e.postData.contents);
  var event = JSON.parse(e.postData.contents).events[0];
  log(event)
  //受信したメッセージ情報を変数に格納する
  var replyToken　= json.events[0].replyToken; //reply token
  var messageId = json.events[0].message.id; //メッセージID
  var messageType = json.events[0].message.type; //メッセージタイプ
  var userId = event.source.userId;
  var _message = json.events[0].message.text;
  var latitude = json.events[0].message.latitude;
  var longitude = json.events[0].message.longitude;
  
  if(messageType === "location"){
    var sum = latitude + longitude;
    GetNearestShop(sum,replyToken);
  }else{
    log("none")  
  }
}


function GetNearestShop(sum,replyToken){
  var spreadsheet = SpreadsheetApp.openById('id');
  var ss = spreadsheet.getSheetByName('シート名');
  var lastRow = ss.getLastRow();
  var test = null;
  var currentDiff = 999999;
  var name = null;
  var area = null;
  var takeout = null;
  var deli = null;
  var hp = null;
  var jun = null;
  var address = null;
  for (var i=2; i<lastRow+1; i++){
    if (ss.getRange(i, 11).getValue()  !== ""){
      test = ss.getRange(i, 11).getValue();
      //この時点での近似値と、指定値の差異を絶対値で保持しておく
      var closestDiff = Math.abs(sum - test);
      //新しく比較した値のほうが近かったら、近似値として保持しておく
      if(currentDiff > closestDiff){
        currentDiff = closestDiff;
        area = ss.getRange(i, 1).getValue();
        jun = ss.getRange(i, 2).getValue(); 
        takeout = ss.getRange(i, 3).getValue();
        deli = ss.getRange(i, 4).getValue();   
        hp = ss.getRange(i, 5).getValue();  
        name = ss.getRange(i, 7).getValue();
        address = ss.getRange(i, 8).getValue();
      }
     continue;
    }
  }
  log(address)
  log(area)
 reply(area,jun,takeout,deli,hp,name,address,replyToken);
}

function reply(area,jun,takeout,deli,hp,name,address,replyToken){
const CHANNEL_ACCESS_TOKEN = "アクセストークン";
const line_endpoint = 'https://api.line.me/v2/bot/message/reply';
 log("here")
 UrlFetchApp.fetch(line_endpoint, {
      'headers': {
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer ' + CHANNEL_ACCESS_TOKEN,
      },
      'method': 'post',
      'payload': JSON.stringify({
        'replyToken': replyToken,
        'messages': [{
          'type': 'text',
          'text': "[gotoeat対応店舗]\n名前:" + name + "\nジャンル: " + jun + "\nエリア: " + area + "\nテイクアウト: " + takeout + "\nデリバリー: " + deli + "\nHP: " + hp +　"\n所在地:" + address,
        }],
      }),
    });
  log("after")
}

function log(message){
  var spreadsheet = SpreadsheetApp.openById('シートid');
  var ss = spreadsheet.getSheetByName('log');
  ss.appendRow([message]);
}

```

##住所から経度緯度を無料で取得するコード

```javascript:geo.js
function geocoder2() {
    const START_ROW = "開始行";
    const FACILITY_COL = 1;
    const LNG_COL = 2;
    const LAT_COL = 3;
    
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート名');
    var lastrow = spreadsheet.getLastRow();
     
    for(var i=START_ROW; i<=lastrow; i++){
    
      var facility = spreadsheet.getRange(i,FACILITY_COL).getValue();
     
      var geocoder = Maps.newGeocoder();
      geocoder.setLanguage('ja');
     
      var response = geocoder.geocode(facility);
       
      if(response['results'][0] != null){
        spreadsheet.getRange(i,LAT_COL).setValue(response['results'][0]['geometry']['location']['lat']);
        spreadsheet.getRange(i,LNG_COL).setValue(response['results'][0]['geometry']['location']['lng']);
      }
    }
   }
   

```
ちなみにこの".newGeocoder"は1日の使用回数の制限があるみたいで自分が使ったときは1000回くらい使用したら制限が入りました。
なのでまだデータを整形できていない部分があります、、

##課題点
1.現在は一つしかお店が返ってこないので5つくらい近場のを返せるようにしたい
→アルゴリズムを作成して対応しようと考えているが、勉強不足でまだ作成できないみたいなので先に勉強します
ちなみに今現在作成中のコードはこんな感じです、かなり不格好なのですがとりあえず動かそうとしている感じです、また完成したら追記します。
やりたい内容としては差が0に近い順に５つ値を取得して保持できたらなという感じです。

```javascript:get5.js
   
function testGetNearestShop(){
    var spreadsheet = SpreadsheetApp.openById('シートid');
    var ss = spreadsheet.getSheetByName('シート名');
    var lastRow = ss.getLastRow();
    var test = null;
    //これは適当
    var sum = 130 ;
    //ここも不格好なので変更したい
    var currentDiff = 999993;
    var currentDiff2 = 999994;
    var currentDiff3 = 999995;
    var currentDiff4 = 999996;
    var currentDiff5 = 999997;
    
    var p = null;
    var p1 = null;
    var p2 = null;
    var p3 = null;
    var p4 = null;
    //null
    var namenull = null;
    //o
    var name = null;
    //1
    var name1 = null;
    //2
    var name2 = null;
    //3
    var name3 = null;
    //4
    var name4 = null;
    
    for (var i=2; i<lastRow+1; i++){
      if (ss.getRange(i, 11).getValue()  !== ""){
        test = ss.getRange(i, 11).getValue();
        //この時点での近似値と、指定値の差異を絶対値で保持しておく
        var closestDiff = Math.abs(sum - test);
        //新しく比較した値のほうが近かったら、近似値として保持しておく
        if(currentDiff > closestDiff){
          currentDiff = closestDiff;
          //もしdiff2がdiffより差異がない場合は
          if(currentDiff2 > currentDiff){
            p = currentDiff2;
            currentDiff2 = currentDiff;
            currentDiff = p;
            //もしdiff2がdiffより差異がない場合は
            if(currentDiff3 > currentDiff2){   
              p = currentDiff3;
              log("p is " + p);
              log("currentDiff3 is " + p);
              p1 = currentDiff2;
              log("p1 is " + p1);
              log("currentDiff2 is " + currentDiff2)
              currentDiff3 = currentDiff2;
              log("currentDiff3 is " + currentDiff3)
              log("currentDiff2 is " + currentDiff2)
              currentDiff2 = p;
              log("currentDiff2 is " + currentDiff2)
              log("p is " + p);
              currentDiff = p1;
              log("currentDiff is " + currentDiff)
              log("p1 is " + p1);
              /*if(currentDiff4 > currentDiff3){  
                p = currentDiff4;
                p1 = currentDiff3;
                p2 = currentDiff2;
                currentDiff4 = currentDiff3;
                currentDiff3 = p;
                currentDiff2 = p1;
                currentDiff = p2;
                if(currentDiff4 > currentDiff3){
                  p = currentDiff5;
                  p1 = currentDiff4;
                  p2 = currentDiff3;
                  p3 = currentDiff2;
                  p4 = currentDiff;
                  currentDiff5 = currentDiff4;
                  currentDiff4 = p;
                  currentDiff3 = p1;
                  currentDiff2 = p2;
                  currentDiff = p3;
                }
              }*/
            }
        }
       continue;
      }
    }
    //log(currentDiff5)
    //log(currentDiff4)
    
   //reply(area,jun,takeout,deli,hp,name,replyToken);
  }
    log("test>" + currentDiff3)
    log("test1>" + currentDiff2)
    log("test2>" + currentDiff)
  }
  
```

2.データの取得をapi作成して取得できないかやってみる
→現在の仕様はあまりに効率が悪いので

3.近場の取得を経度と緯度の差異ではなくもっとスマートな形で取得したい
半径1km以内みたいな

4.処理が激重なのでデータ探索のアルゴリズムを正しく生成するとともに県ごとにシートをわけて条件分岐で処理したい。


##最後に
現在私は個人事業主として下記のサイトの事業を取り進めております。(サイトはまだ作成して間もないのでまだガタガタです、、)
ホテルの自動チェックインのラインでの実装や、rpaの制作をメインに進めています。
作業の自動化等にご興味があれば是非ご連絡ください。何卒よろしくお願いいたします。
https://senren.work/

##追記
もしここが使いづらい、こうして欲しいというご意見があれば是非コメントお願いします