何をするのか
- RememberTheMilkのタスクをスプレッドシートに取得する
なぜするのか
- GoogleSpreadSheetから何かのAPIを使ってみたかった
- GrideAppなど連携すると楽しいかもと思った
RememberTheMilk API keyの取得
- 以下のURLからNon-Commercial use のAPIkeyとSecretを取得
GAS
設定関連
// AuthParams
var apiKey = "apikey_xxxxxxxxxxxx"
var secret = "secret_xxxxxxxxx"
// SpreadSheetSettings
var spreadsheetId = "spreadsheetid_xxxxxxxxxxxxxx"
var spreadsheetSettingSheetname = "Settings"
var spreassheetOutputSheetname = "Output"
var spreadsheet = SpreadsheetApp.openById(spreadsheetId)
var sheet = spreadsheet.getSheetByName(spreadsheetSettingSheetname)
var tokenRange = sheet.getRange(1,2)
//requestUrls
var restBaseUrl = "https://api.rememberthemilk.com/services/rest/"
var authBaseUrl = "https://www.rememberthemilk.com/services/auth/"
//Parameters
var listId = xxxxxxx;//取得したいTaskListのId
Mainとなるフロー
- Taskの取得のフローは下記
- tokenが切れていたら再度tokenを取得、切れていなければそのままtokenを使う
- token期限が切れているかを確認 function checkToken(token)
- frobを取得 function getFlob()
- frobを使って認証する function auth(flob)
- frobをtokenに交換(この時点でfrobが破棄される) function getToken(frob)
- 以降はtokenを使ってapiを呼び出す function getTaskList(token, listId)
- tokenが切れていたら再度tokenを取得、切れていなければそのままtokenを使う
- 定期的にタスクを取得するためには、この関数を定期実行する
- とりあえず1シートを更新するプログラムになっているが、日付毎にシートを作って履歴管理をするのもありかも
function getTasksFromRtm() {
token = tokenRange.getValue()
if (checkToken(token) == false){
//tokenの期限が切れている場合、新しく認証する(URLが表示されるのでブラウザ上でアクセスしたあと、OKボタン)
var frob = getFrob()
var authResult = auth(frob)
var token = getToken(frob)
tokenRange.setValue(token) //tokenをシートに保持
}
var taskList = getTaskList(token, listId)
//Logger.log(tasks)
var tasks = taskList[0]["taskseries"]
toSpreadSheet(spreassheetOutputSheetname, tasks)
//Logger.log(tasks[0]["taskseries"][0])
}
Module
- 全体として、API呼び出しをするときのルールがある
- リクエストパラメータをmd5でハッシュ化したauth_sigを付与する必要がある
- 例えば、「?method=aaa&list_id=bbb&auth_key=ccc」の場合、下記(※パラメータ名のアルファベット順に並び替えてkeyvalueを連結したもの)をmd5でハッシュ化する。
- 「auth_keyccclist_idbbbmethodaaa」
- ハッシュ化したものを「zzzzzz」とすると、最終的なリクエストパラメータは下記となる
- 「?method=aaa&list_id=bbb&auth_key=ccc&auth_sig=zzzzzz」となる
取得したタスクをスプレッドシートに転記
function toSpreadSheet(outputSheetname, tasks){
var outputSheet = spreadsheet.getSheetByName(outputSheetname)
outputSheet.clear()
//転記するサイズ
var rowSize = tasks.length
//Header
outputSheet.getRange(1, 1).setValue("id")
outputSheet.getRange(1, 2).setValue("title")
outputSheet.getRange(1, 3).setValue("completed")
for(var i = 1; i < rowSize + 1 ; i++){
outputSheet.getRange(i+1,1).setValue(tasks[i-1].id);
outputSheet.getRange(i+1,2).setValue(tasks[i-1].name);
outputSheet.getRange(i+1,3).setValue(tasks[i-1]["task"][0].completed); //完了の日付
}
}
function getFrob() //frobを取得する
- 今回、taskの取得だけを目指すのでparams=readとしているが、更新や削除したいときはwrite/deleteを使う
function getFrob(){
//frobをjson形式で取得する
var methodToGetfrob = "rtm.auth.getFrob"
var getFrobUrl = restBaseUrl + "?method=" + methodToGetfrob + "&api_key=" + apiKey + "&format=json&perms=read" //読み取りのみ
var frobSign = secret + "api_key" + apiKey + "formatjson" + "method"+ methodToGetfrob + "permsread"
var encoded = MD5(frobSign)
getFrobUrl = getFrobUrl + "&api_sig=" + encoded
Logger.log(getFrobUrl);
var frobJson = UrlFetchApp.fetch(getFrobUrl).getContentText()
var json = JSON.parse(frobJson);
if (json["rsp"]["stat"] == "ok") {
frob = json["rsp"]["frob"]
}
Logger.log(frob)
return frob
}
function checkToken() //tokenが切れているかどうかを取得する
function checkToken(token){
var methodToCheckToken = "rtm.auth.checkToken"
var getCheckTokenUrl = restBaseUrl + "?method=" + methodToCheckToken + "&api_key=" + apiKey + "&auth_token=" + token +"&format=json" //読み取りのみ
var checkTokenSign = secret + "api_key" + apiKey+ "auth_token"+ token + "formatjson" + "method"+ methodToCheckToken
Logger.log(checkTokenSign)
encoded = MD5(checkTokenSign)
getCheckTokenUrl = getCheckTokenUrl + "&api_sig=" + encoded
Logger.log(getCheckTokenUrl)
var checktokenJson = UrlFetchApp.fetch(getCheckTokenUrl).getContentText()
var json = JSON.parse(checktokenJson);
if (json["rsp"]["stat"] == "ok") {
//tasks = json["rsp"]["tasks"]["list"]
Logger.log(checktokenJson);
return true
}
Logger.log(checktokenJson);
return false
}
function getTaskList(token, tasklistId) //taskを取得する
function getTaskList(token, tasklistId){
var methodToGetTask = "rtm.tasks.getList"
var getTaskUrl = restBaseUrl + "?method=" + methodToGetTask + "&api_key=" + apiKey + "&auth_token=" + token + "&list_id=" + tasklistId +"&format=json" //読み取りのみ
var taskSign = secret + "api_key" + apiKey+ "auth_token"+ token + "formatjson" + "list_id" + tasklist_id + "method"+ methodToGetTask
Logger.log(taskSign)
encoded = MD5(taskSign)
getTaskUrl = getTaskUrl + "&api_sig=" + encoded
Logger.log(getTaskUrl)
var taskJson = UrlFetchApp.fetch(getTaskUrl).getContentText()
var json = JSON.parse(taskJson);
if (json["rsp"]["stat"] == "ok") {
tasks = json["rsp"]["tasks"]["list"]
return tasks
}
Logger.log(taskJson);
return false
}
function auth(frob) //認証する
- GASだけではうまく認証できなかったので、次のような手順で手動認証
- tokenが無効の時、認証用のURLをDialogに表示する
- (手動)認証用のURLをブラウザに貼り付け、アクセス許可をする
- DialogでOKを押すと以降の処理が進む
//Auth Miyuki Kondo 2020/12/28
function auth(frob){
var authUrl = authBaseUrl + "?api_key=" + apiKey + "&frob=" + frob + "&format=json&perms=read" //読み取りのみ
var authSign = secret + "api_key" + apiKey + "formatjson" + "frob" + frob + "permsread"
encoded = MD5(authSign)
authUrl = authUrl + "&api_sig=" + encoded
Logger.log(authUrl);
//下記ではAuthできなかったので、Msgに出るURLを開く形式に。
//var auth = UrlFetchApp.fetch(authUrl, {method:"post"}).getContentText()
//Logger.log(auth)
//Authしていない場合は、ブラウザでアクセスして許可、DialogでOKが出るまでWaitする。
var result = Browser.msgBox(authUrl, Browser.Buttons.OK_CANCEL)
return true
}
function getToken(frob) //認証する
function getToken(frob){
//tokenをjson形式で取得する
var methodToGetToken = "rtm.auth.getToken"
var getTokenUrl = restBaseUrl + "?method=" + methodToGetToken + "&api_key=" + apiKey + "&frob=" + frob +"&format=json&perms=read" //読み取りのみ
var tokenSign = secret + "api_key" + apiKey + "formatjson" + "frob" + frob + "method"+ methodToGetToken + "permsread"
encoded = MD5(tokenSign)
getTokenUrl = getTokenUrl + "&api_sig=" + encoded
Logger.log(getTokenUrl)
var tokenJson = UrlFetchApp.fetch(getTokenUrl).getContentText()
var json = JSON.parse(tokenJson);
if (json["rsp"]["stat"] == "ok") {
token = json["rsp"]["auth"]["token"]
Logger.log(tokenJson)
Logger.log(token)
return token
}
Logger.log(tokenJson)
return false
}
- MD5関数は下記サイトをそのままなので本家からご参照ください。
//MD5を求める関数。下記サイトより拝借。
//https://qiita.com/SogoK/items/cc0d514ffe74009e5fd5
function MD5(input) {
...
}
まとめ
- 結局AuthをGASだけではできなかった。
- token保持しておけば毎回認証する必要はないため、定期実行はできそう(GoogleDrive等々にアクセスする際に認証を求められるのと同じ形式かな)
- (tokenの保持をスプレッドシートにしちゃってるのはどうなのかと思う、、、、)
- 貼り付けてみたら変数名がキャメルケースとスネークケースがばらんばらんで慌てて直した。jsではキャメルケースが一般的っぽいですかね。
- もうGASからのAPIコワクナイ