Skip to content

Instantly share code, notes, and snippets.

@kazuhito-m
Last active September 24, 2020 15:54
Show Gist options
  • Save kazuhito-m/a7d5b045f93d40375af1ebb9140e8fd1 to your computer and use it in GitHub Desktop.
Save kazuhito-m/a7d5b045f93d40375af1ebb9140e8fd1 to your computer and use it in GitHub Desktop.
KaraokeTweet.gs
/**
* JoySoundの歌唱履歴ページを定期的にポーリングし、追加があればSpreadSheetに書きつつ、TwitterへつぶやくGASスクリプト。
*
* Usage
* 1. GoogleAppScriptに新たにスクリプトを作り、このソースを貼る。
* 2. ファイル->プロジェクトのプロパティ->スクリプトのプロパティを開き、以下の変数を設定する。
* - joysound_id : JoySoundの「うたスキ(https://www.joysound.com/utasuki)」にログインするためのID
* - joysound_password : 上記のパスワード
* - spreadsheet_id : GoogleSpreadSheetのID
* - twitter_api_key : TwitterのAPI key
* - twitter_api_secret_key : TwitterのAPI secret key
* - twitter_access_token : TwitterのAccess token
* - twitter_access_token_secret : TwitterのAccess token secret
* - user_nick_name : Tweetに表示するユーザ名の愛称
* 3. 編集->現在のプロジェクトのトリガー を選択、トリガー画面で「トリガーの追加」をクリックし、以下の条件で鶏が設定する
* - 実行する関数を選択 : main
* - イベントのソースを選択 : 時間手動型
* - 時間ベースのトリガーのタイプを選択 : 分ベースのタイマー
* - 時間の間隔を選択(分) : 15分おき
*/
function getKaraokeHistory(mailAddress, password) {
// Login
const url = 'https://www.joysound.com/utasuki/login.htm?path=%252Futasuki%252Fmypage%252Fhistory%252Findex.htm&redirect=true';
const response = UrlFetchApp.fetch(url, {
"payload": 'loginId=' + mailAddress + '&password=' + password,
"method": "POST",
'followRedirects': false,
});
const httpStatus = response.getResponseCode();
if (httpStatus !== 302) {
console.log('Failed. HTTP status: ' + httpStatus);
return;
}
const returnedCookies = response.getAllHeaders()['Set-Cookie'];
// History Api call
const cookies = returnedCookies
.map(cookie => cookie.replace(/;.*/, ''))
.join('; ');
const headers = {
"Cookie" : cookies,
'X-JSP-APP-NAME': 'www.joysound.com',
'Referer': 'https://www.joysound.com/utasuki/mypage/history/index.htm',
}
const apiUrl = 'https://www.joysound.com/api/1.0/member/@me/karaokeHistory?count=20&orderBy=0&sortOrder=desc&startIndex=0&maxPageNum=1';
const apiResponse = UrlFetchApp.fetch(apiUrl, {
'headers': headers
});
return JSON.parse(apiResponse.getContentText());
}
function writeHistorySheet(id, karaokes) {
const spread = SpreadsheetApp.openById(id);
var sheet = spread.getSheets()[0];
const lastRow = sheet.getLastRow();
const value = sheet.getRange(lastRow, 1).getValue();
const lastPlayDateTime = Number.isInteger(value) ? Number.parseInt(value) : 0;
const sortAndFilterd = karaokes
.filter(d => d.playDateTime > lastPlayDateTime)
.sort((l, r) => l.playDateTime - r.playDateTime);
let row = lastRow + 1;
for (let data of sortAndFilterd) {
const song = data.selSong;
const artist = data.artist;
writeOf(sheet, row, 1, data.playDateTime);
writeOf(sheet, row, 2, data.key);
writeOf(sheet, row, 3, song.selSongNo);
writeOf(sheet, row, 4, song.selSongName);
writeOf(sheet, row, 5, song.selSongNameRuby);
writeOf(sheet, row, 6, artist.artistName);
writeOf(sheet, row, 7, artist.artistNameRuby);
row++;
}
return sortAndFilterd;
}
function writeOf(sheet, row, col, value) {
sheet.getRange(row, col).setValue(value);
}
function formatOf(date) {
const d = date;
return d.getFullYear() +
'/' + zp(d.getMonth() + 1) +
'/' + zp(d.getDate()) +
' ' + zp(d.getHours()) +
':' + zp(d.getMinutes()) +
':' + zp(d.getSeconds());
}
function zp(num) {
return ("00" + num).slice(-2);
}
function executeTwitterApi(url,method,body) {
// プロパティ取得
const properties = PropertiesService.getScriptProperties();
const API_KEY = properties.getProperty('twitter_api_key');
const CONSUMER_SECRET = properties.getProperty('twitter_api_secret_key');
const ACCESS_TOKEN = properties.getProperty('twitter_access_token');
const TOKEN_SECRET = properties.getProperty('twitter_access_token_secret');
// 認証情報作成
const date = new Date();
const timestamp = date.getTime().toString().substr(0,10);
const nonce = timestamp; // 実行毎に一意であれば何でもいい
let authorizationParameters = [
'oauth_consumer_key=' + API_KEY,
'oauth_nonce=' + nonce,
'oauth_signature_method=HMAC-SHA1',
'oauth_timestamp=' + timestamp,
'oauth_token=' + ACCESS_TOKEN,
'oauth_version=1.0'
];
const encodedUrl = encodeURI(url)
let baseUrl = encodedUrl
let queryParams = new Array();
if(encodedUrl.match(/\?/)){
baseUrl = encodedUrl.split('?')[0]
queryParams = encodedUrl.split('?')[1].split('&')
queryParams.forEach(function(param){
authorizationParameters.push(param);
})
authorizationParameters = authorizationParameters.sort()
Logger.log(queryParams)
}
// Signature作成
const signature_base_string = method + '&' + encodeURIComponent(baseUrl) + '&' + encodeURIComponent(authorizationParameters.join('&'));
const signature_key = encodeURI(CONSUMER_SECRET) + '&' + encodeURI(TOKEN_SECRET);
const signature = encodeURIComponent(Utilities.base64Encode(Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, signature_base_string, signature_key)));
const authorization = 'OAuth ' + authorizationParameters.join(',') + ',oauth_signature=' + signature;
// APIパラメータ作成
const parameters = {
method : method,
headers : {"Authorization": authorization},
muteHttpExceptions : true
};
// API実行
const response = UrlFetchApp.fetch(encodedUrl,parameters);
return response
}
function tweeting(text){
const url = "https://api.twitter.com/1.1/statuses/update.json?status=" + text;
const method = 'POST';
executeTwitterApi(url,method)
}
function tweetHistory(histories, nickName) {
let firstTime = true;
for (let h of histories) {
if (!firstTime) Utilities.sleep(3000);
firstTime = false;
console.log(h);
const dateText = formatOf(new Date(h.playDateTime));
const keyText = 'Key:' + h.key.replace('+', '+').replace('-', 'ー');
const base = `${nickName}は${dateText}に「${h.selSong.selSongName}/${h.artist.artistName}」を${keyText}で歌いました。 #カラオケ歌うとつぶやかれるヤツ`;
tweet = base.replace('(', '(').replace(')', ')');
tweeting(tweet);
}
}
function main() {
const properties = PropertiesService.getScriptProperties();
const KARAOKE_ID = properties.getProperty('joysound_id');
const KARAOKE_PASSWORD = properties.getProperty('joysound_password');
const SPREADSHEET_ID = properties.getProperty('spreadsheet_id');
const USER_NICK_NAME = properties.getProperty('user_nick_name');
const histories = getKaraokeHistory(KARAOKE_ID, KARAOKE_PASSWORD);
const registerd = writeHistorySheet(SPREADSHEET_ID, histories.myKaraokes);
if (registerd.length > 0) tweetHistory(registerd, USER_NICK_NAME);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment