Skip to content

Instantly share code, notes, and snippets.

@knz21

knz21/tweet_composition.gs

Last active Sep 23, 2019
Embed
What would you like to do?
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
You can’t perform that action at this time.