VRChatでフレンドになった日時をGASでGoogleカレンダーに自動記録

VRChat×GAS第二弾

VRChatでフレンドに登録した日時をGoogleカレンダーにGASを用いて記録。
必要なものは[前回](https://qiita.com/wata5f68/items/3766e6cc0b5c896aafa4)同様Googleアカウントのみ。

## 今回のスクリプトの注意点
- すでに登録されているフレンドは記録できない。
- VRChatを遊ばない時間を決めておく必要がある
    - VRC API利用時のサーバー負荷の関係
    - 今回の実装では午前9時から11時まで2時間はVRCを遊ばない想定をしている
- 一日にフレンド登録しすぎるとカレンダーに反映できない可能性
    - 今回のスクリプトの実装では最大でも1日に100名の登録程度
        - 場合によってはもっと少ない    

## VRC API
今回使用するのはフレンドのID一覧を取得できる"[Login](https://vrchatapi.github.io/#/UserAPI/Login)"とIDから表示名を取得できる"[Get By ID](https://vrchatapi.github.io/#/UserAPI/GetByID)"。

Loginエンドポイント
https://api.vrchat.cloud/api/1/auth/user

Get By IDエンドポイント
https://api.vrchat.cloud/api/1/users/[ID]

## 準備

### カレンダーの作成
フレンド登録日時を書き込む用のカレンダーを作成し、IDを記録しておく。

### スプレッドシートをGoogleドライブ上に作成

作ったスプレッドシートの1行目にこんな感じで書き込んでいく。
![6567209d-690f-4d32-8974-8b6342502fb0.jpg](https://qiita-image-store.s3.amazonaws.com/0/332214/f69593ad-e73d-ffb0-0408-a707cb9eb12c.jpeg)
Time, FriendID, FriendName, WorldID, WorldName, FriendNum, NewFriend
(まだ意味を持っていないものもあるけど…)






スプレッドシート上部のツールからスクリプトエディタを起動。

![bindgaspng.png](https://qiita-image-store.s3.amazonaws.com/0/332214/174b0db0-378a-9772-1458-061043b0cef1.png)


## 実装
### 処理の流れ
0.すでにフレンド登録されている人たちのIDをVRC APIの"Login"を使ってスプレッドシートに記録する。(初期化処理)
*0.は手動で1度のみ実行。

1.　時間ベースのトリガーで5分ごとに現在のフレンドID一覧を"Login"を使って取得。
2.　取得したフレンドID一覧とスプレッドシートのID一覧を比較し、スプレッドシートに存在しないものがあれば、新しいフレンド。
3.　2.で取得した新しいフレンドのIDと取得日時一覧をスプレッドシートに書き込む
4.　次の日の午前9時から午前11時までの間に "Get By ID" を用いてフレンドIDからフレンド名を解決するトリガーをセットする。

### スクリプト本体
以下のスクリプトを起動したスクリプトエディタに書き込む。
VRCのアカウントのIDとパスワード(userid, password), 作ったカレンダーのID(calID)を埋めておく。  

初期化処理 (InitFriendInf()) はエディタ上で手動で1度実行しておく。

~~~
//まず、すでに登録されているフレンドをスプレッドシートに書き込む
function InitFriendInf(){
  
  // アクセス先の情報
  var url = "https://api.vrchat.cloud/api/1/auth/user/";
  var userid = "";
  var password = "";  
  var queryString = "?apiKey=JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26";
  

  var SheetName = "シート1";
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var Sheet = ss.getSheetByName(SheetName);
  var dat = Sheet.getDataRange().getValues(); //シートデータを取得
  
  Logger.log(dat.length);
  
  url = url + queryString;
  Logger.log(url);

  // GETメソッドのオプション
  var options = {
    "contentType": "application/json",
    "headers" : {"Authorization" : "Basic " + Utilities.base64Encode(userid + ':' + password, Utilities.Charset.UTF_8)},
    "muteHttpExceptions" : true
  }

  try{
    var response = UrlFetchApp.fetch(url, options);
    var content = response.getContentText("UTF-8");
    Logger.log(content);
    var json=JSON.parse(response);
 
    Logger.log("フレンドリストは" + json["friends"]);
    
    var FriendIDs = json["friends"];

    var now = new Date();
    
    
    for(var i =0; i<FriendIDs.length; i++ ){
      //スプレッドシートに書き込むので1ずれる、かつヘッダーがあるので、合計2を足す
      Sheet.getRange(i+2,1).setValue(now);//日時
      Sheet.getRange(i+2,2).setValue(FriendIDs[i]);//フレンドID
      Sheet.getRange(i+2,3).setValue("Recorded");//フレンド名
      Sheet.getRange(i+2,4).setValue("Recorded");//あったワールドID・未実装
      Sheet.getRange(i+2,5).setValue("Recorded");//あったワールド名・未実装
  }
    //フレンド数を記録
    Sheet.getRange(2,6).setValue(FriendIDs.length);
    
  }catch(e){
    Logger.log(e.message);
  } 
}


function FindNewFriend(){
  
  var now = new Date();
  var nowHour = now.getHours();
  
  
  //9時から11時はフレンド解決の時間
  if( (nowHour == 9) || (nowHour == 10) ){
    return 0;
  }
  
  
  
  // アクセス先の情報
  var url = "https://api.vrchat.cloud/api/1/auth/user/";
  var userid = "";
  var password = "";  
  var queryString = "?apiKey=JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26";
  
  var SheetName = "シート1";
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var Sheet = ss.getSheetByName(SheetName);
  var dat = Sheet.getDataRange().getValues(); //シートデータを取得
  

  
  url = url + queryString;

  // GETメソッドのオプション
  var options = {
    "contentType": "application/json",
    "headers" : {"Authorization" : "Basic " + Utilities.base64Encode(userid + ':' + password, Utilities.Charset.UTF_8)},
    "muteHttpExceptions" : true
  }

  try{
    var response = UrlFetchApp.fetch(url, options);
    var content = response.getContentText("UTF-8");
    Logger.log(content);
    var json=JSON.parse(response);
 
    Logger.log("フレンドリストは" + json["friends"]);
    
    var FriendIDs = json["friends"];

    var now = new Date();
    
 
    var NewFriendIDs = [];
    //比較
    
    for(var i=0; i<FriendIDs.length; i++ ){
      for(var j=1; j<dat.length; j++){
        if(dat[j][1] == FriendIDs[i]){//もし同じIDがすでに登録済みから見つかったら
          break;
        }
        if(j == (dat.length-1)){//もし最後までみつからなかったら新しいフレンド
          NewFriendIDs.push(FriendIDs[i]);
        }
      }
    }
    
    Logger.log(NewFriendIDs);
    
    if(NewFriendIDs.length >  0){//もし新しいフレンドがいるなら
      //スプレッドシートの一番後ろから登録
      for(var i=0;i<NewFriendIDs.length;i++){
        Sheet.getRange(i+(1+dat.length),1).setValue(now);//フレンドID
        Sheet.getRange(i+(1+dat.length),2).setValue(NewFriendIDs[i]);//フレンドID
      }
     
      
      //後で実装する
      //FriendIDs[0]を使っててワールドIDをGetBYIDで問い合わせする関数を1分後にトリガー
      //今回登録されたフレンドは同じワールドID
      
      //フレンド名解決のトリガーを明日9時にセット
      var triggerDay = new Date();
      triggerDay.setDate(triggerDay.getDate()+1);
      triggerDay.setHours(9);
      triggerDay.setMinutes(5);
      ScriptApp.newTrigger("ResolveFriendName").timeBased().at(triggerDay).create();   
    }
    
  }catch(e){
    Logger.log(e.message);
  } 
}

function ResolveFriendName(){
  deleteTrigger("ResolveFriendName");
  var now = new Date();
  var nowHour = now.getHours();
  
  //自分がVRCをやらないであろう時間にトリガーセット
  //9時から11時はフレンド解決の時間
  if( !((nowHour == 9) || (nowHour == 10)) ){
   return 0;
  }
  
  // アクセス先の情報
  var url = "https://api.vrchat.cloud/api/1/users/";
  var userid = "";
  var password = "";  
  var queryString = "?apiKey=JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26";
  

  var SheetName = "シート1";
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var Sheet = ss.getSheetByName(SheetName);
  var dat = Sheet.getDataRange().getValues(); //シートデータを取得
  
  //フレンドIDはあるがフレンド名はないフレンドを探す
  //前回のスプレッドシートのフレンド数をとってくる
  var FriendNum = dat[1][5];
  Logger.log(FriendNum);
  
  
  //フレンドネームを解決
  var FriendID = dat[FriendNum+1][1];
  url = url + FriendID + queryString;
  
  var options = {
    "contentType": "application/json",
    "headers" : {"Authorization" : "Basic " + Utilities.base64Encode(userid + ':' + password, Utilities.Charset.UTF_8)},
    "muteHttpExceptions" : true
  }

  try{
    var response = UrlFetchApp.fetch(url, options);
    var content = response.getContentText("UTF-8");
    Logger.log(content);
    var json=JSON.parse(response);
 
    Logger.log("ユーザ名は" + json["displayName"]);
    var FriendName = json["displayName"];
    
    Sheet.getRange(FriendNum+2, 3).setValue(FriendName);
    
    
    //カレンダーに追加
    
    var TheTime = Sheet.getRange((FriendNum+2), 1).getValue();
    Logger.log(TheTime);
    RecordCalender(FriendName, TheTime);
    
    //FriendNumを1増加
    FriendNum++;
    Sheet.getRange(2,6).setValue(FriendNum);

    
    //1回呼び出されるたびに1名のフレンド名を解決
    //もし名前未解決のフレンドがいるなら IDが空文字でない
    if( (dat[FriendNum+1][1] != "") && (dat[FriendNum+1][2] == "")){
      //再度トリガーを作る
      //set Triger 1分後に起動
      var triggerDay = new Date();
      triggerDay.setMinutes(triggerDay.getMinutes() +1);
      ScriptApp.newTrigger("ResolveFriendName").timeBased().at(triggerDay).create();   
    }
    
  }catch(e){
    Logger.log(e.message);
  } 
}

function RecordCalender(FriendName, TheTime){
  var EventName = FriendName + "と友達になった"
  var calID =  "";
  var cal = CalendarApp.getCalendarById(calID);
  var now = TheTime;
  cal.createEvent(EventName, now, now);  
}

function deleteTrigger(TriggerName) {
  var triggers = ScriptApp.getProjectTriggers();
  for(var i=0; i < triggers.length; i++) {
    if (triggers[i].getHandlerFunction() == TriggerName) {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}
~~~

## トリガーの設定
トリガーの設定は以下の通り。現在のフレンド一覧を取得し、スプレッドシートと比較する関数を5分ごとに実行する。
![a7311939-8abf-482f-98d9-5f6693ec38a8.jpg](https://qiita-image-store.s3.amazonaws.com/0/332214/9763d539-59e5-5d81-b0f5-cc9301cf09ea.jpeg)

## 結果

Googleカレンダーに以下のように表示されるようになる。
フレンドの皆様、ご協力ありがとうございました。

![be366ae3-c011-4391-b34b-682e8ca4451a.jpg](https://qiita-image-store.s3.amazonaws.com/0/332214/642e013a-91d7-1844-fb66-8d6ea26bb594.jpeg)


## 今後やりたいこと
フレンドになったワールドの名前を記録したい。