TANITAの体組成計連携Tweetを元に作成したグラフを投稿
// HealthPlanet側のTwitterテンプレート '体重:#WHT#kg 体脂肪率:#BFP#% 筋肉量:#MBK#kg 筋肉スコア:#MBS# 内臓脂肪レベル:#VFL# 基礎代謝量:#BAR# 体内年齢:#BDA#歳 推定骨量:#APB#' | |
var SPREADSHEET_ID = '保存先スプレッドシートのID' | |
var COL_INDEX = { | |
'date': 1, | |
'weight': 2, | |
'fat': 3, | |
'muscle': 4, | |
'muscle_score': 5, | |
'visceral_fat': 6, | |
'metabolism': 7, | |
'body_age': 8, | |
'bone': 9 | |
} | |
var COMPOSITION_COLUMN_SIZE = 8 | |
var CHART_ROW_SIZE = 28 | |
var CHART_COLUMN_SIZE = 3 | |
var SHEET = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName('シート名') | |
// trigger begin | |
// グラフをtwitterに投稿 (週次とか) | |
function tweetChartImage() { | |
const twitterService = getTwitterService() | |
if (twitterService.hasAccess()) { | |
const params = { | |
'status': 'グラフ #tanita #healthplanet', | |
'media_ids': [ | |
uploadImage(twitterService, createChart(COL_INDEX.weight).getBlob()), | |
uploadImage(twitterService, createChart(COL_INDEX.fat).getBlob()), | |
uploadImage(twitterService, createChart(COL_INDEX.muscle).getBlob()), | |
uploadImage(twitterService, createChart(COL_INDEX.metabolism).getBlob()) | |
] | |
} | |
const sendoption = { | |
'method': 'POST', | |
'payload': Object.keys(params).map(function(key) { | |
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); | |
}).join('&') | |
} | |
twitterService.fetch('https://api.twitter.com/1.1/statuses/update.json', sendoption) | |
} else { | |
Logger.log(service.getLastError()) | |
} | |
} | |
// 体組成をスプレッドシートへ (毎日くらい) | |
function updateDailyComposition() { | |
var twitterService = getTwitterService(); | |
if (twitterService.hasAccess()) { | |
var url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?user_id=TwitterのID&count=20&trim_user=t' // @TwitterのIDの最新20tweetを取得 | |
var response = JSON.parse(twitterService.fetch(url, { method: 'GET' })) | |
var newRows = response | |
.filter(function (tweet) { return isComposition(tweet) && isNew(tweet) }) | |
.map(function (tweet) { return createRow(tweet.created_at, tweet.text) }) | |
if (newRows.length > 0) { | |
SHEET.insertRows(2, newRows.length) | |
SHEET.getRange(2, 1, newRows.length, newRows[0].length).setValues(newRows) | |
calcMovingAverage(newRows.length) | |
} | |
} else { | |
Logger.log(service.getLastError()) | |
} | |
} | |
// trigger end | |
function isComposition(tweet) { | |
return tweet.text.indexOf('#tanita') >= 0 && tweet.text.indexOf('体重:') == 0 | |
} | |
function isNew(tweet) { | |
try { | |
return new Date(tweet.created_at).getTime() > SHEET.getRange(2, 1).getValue().getTime() | |
} catch (e) { | |
return true | |
} | |
} | |
/** | |
* 保存用のrowを作成 | |
* @param createdAt Date 投稿時間 | |
* @param text String tweet本文: '体重:60.00kg 体脂肪率:15.00% ...' | |
* @returns {Array.<*>} [投稿時間, 60.00, 15.00, ...] | |
*/ | |
function createRow(createdAt, text) { | |
return [ new Date(createdAt) ].concat( | |
text | |
.replace(/kg/g, '').replace('%', '').replace('歳', '').split(' ') | |
.filter(function (element) { return element.indexOf(':') > 0 }).map(function (value) { return value.split(':')[1] }) | |
) | |
} | |
function uploadImage(twitterService, image) { | |
var options = { 'method': 'POST', 'payload': {'media_data': Utilities.base64Encode(image.getBytes())} } | |
return JSON.parse(twitterService.fetch('https://upload.twitter.com/1.1/media/upload.json', options))['media_id_string'] | |
} | |
function calcMovingAverage(newRowSize) { | |
for (var i = 0; i < newRowSize; i++) { | |
SHEET.getRange(4 + i, COMPOSITION_COLUMN_SIZE + 2, 1, COMPOSITION_COLUMN_SIZE).setValues([transpose(SHEET.getRange(2 + i, 2, 5, COMPOSITION_COLUMN_SIZE).getValues()).map(function (nums) { return avarage(nums) })]) | |
} | |
} | |
function transpose(array) { | |
return array[0].map(function (c, i) { return array.map(function (r) { return r[i] }) }) | |
} | |
function avarage(nums) { | |
return nums.reduce(function (a, c) { return a + c }) / nums.length | |
} | |
function createChart(targetIndex) { | |
var tmpSheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName('グラフ描画に使うシート名') | |
copyToTmp(tmpSheet, targetIndex) | |
return SHEET.newChart() | |
.addRange(tmpSheet.getRange(1, 1, CHART_ROW_SIZE, CHART_COLUMN_SIZE)) | |
.setChartType(Charts.ChartType.LINE) | |
.setPosition(2, 2, 0, 0) | |
.setOption('title', SHEET.getRange(1, targetIndex).getValue()) | |
.build(); | |
} | |
function copyToTmp(tmpSheet, targetIndex) { | |
copyColumn(tmpSheet, COL_INDEX.date, 1) | |
copyColumn(tmpSheet, targetIndex, 2) | |
copyColumn(tmpSheet, targetIndex + COMPOSITION_COLUMN_SIZE, 3) | |
} | |
function copyColumn(tmpSheet, targetIndex, destIndex) { | |
tmpSheet.getRange(1, destIndex, CHART_ROW_SIZE, 1).setValues(SHEET.getRange(1, targetIndex, CHART_ROW_SIZE, 1).getValues()) | |
} | |
// twitter begin | |
// Twitter AppのConsumer Api Key | |
var CONSUMER_KEY = PropertiesService.getScriptProperties().getProperty('CONSUMER_KEY'); | |
var CONSUMER_SECRET = PropertiesService.getScriptProperties().getProperty('CONSUMER_SECRET'); | |
// 認証URLを取得しログに出力する | |
function logAuthorizeUri() { | |
var twitterService = getTwitterService(); | |
Logger.log(twitterService.authorize()); | |
} | |
// OAuth認証サービスクラスのインスタンス生成・取得 | |
function getTwitterService() { | |
return OAuth1.createService('Twitter') | |
.setAccessTokenUrl('https://api.twitter.com/oauth/access_token') | |
.setRequestTokenUrl('https://api.twitter.com/oauth/request_token') | |
.setAuthorizationUrl('https://api.twitter.com/oauth/authenticate') | |
.setConsumerKey(CONSUMER_KEY) | |
.setConsumerSecret(CONSUMER_SECRET) | |
// リダイレクト時に実行されるコールバック関数を指定する | |
.setCallbackFunction('authCallback') | |
// アクセストークンを保存するPropertyStoreを指定する | |
.setPropertyStore(PropertiesService.getUserProperties()); | |
} | |
// リダイレクト時に実行されるコールバック関数 | |
function authCallback(request) { | |
var twitterService = getTwitterService(); | |
// ここで認証成功時にアクセストークンがPropertyStoreに保存される | |
var isAuthorized = twitterService.handleCallback(request); | |
if (isAuthorized) { | |
return HtmlService.createHtmlOutput('Success'); | |
} else { | |
return HtmlService.createHtmlOutput('Denied'); | |
} | |
} | |
// twitter end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment