Last active
May 2, 2018 05:29
-
-
Save 17number/aba0e6f64fd1d4bd73cb91b390f31c42 to your computer and use it in GitHub Desktop.
はてなブログの過去記事を定期的にGASでツイート
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ====== 各種変数 ====== */ | |
// Twitter APIキー・シークレット | |
var CONSUMER_KEY = 'YourConsumerKey'; | |
var CONSUMER_SECRET = 'YourConsumerSecret'; | |
var twitter = TwitterWebService.getInstance( | |
CONSUMER_KEY, | |
CONSUMER_SECRET | |
); | |
// ブログ情報 | |
var BLOG_URL = 'YourBlogURL'; | |
// 過去記事Botの宣伝に協力OK? | |
var PROMOTE_OK = true; | |
// 記事一覧を書き出すシート | |
var TARGET_SHEET = "シート1"; | |
// ツイート対象から除外するシート | |
var EXCLUDE_SHEET = "シート2"; | |
// 投稿時刻を正確に時間指定したい人向け(時分を入力、13:05 → "1305") | |
// 注意事項: | |
// ・setTrigger の前に deleteTrigger を必ず実行すること | |
// ・setTrigger の動作時刻より、後の時刻を設定すること | |
var POST_TIMES = [ | |
"2100", | |
"2105", | |
"2110", | |
"2115", | |
"2120", | |
"2125", | |
"2130", | |
]; | |
/* ====== Twitter 関連 ====== */ | |
// 認証 | |
function authorize() { | |
twitter.authorize(); | |
} | |
// 認証解除 | |
function reset() { | |
twitter.reset(); | |
} | |
// 認証後のコールバック | |
function authCallback(request) { | |
return twitter.authCallback(request); | |
} | |
// ツイートを投稿 | |
function postUpdateStatus(tweetString, tweetID) { | |
var service = twitter.getService(); | |
var tweetID = (tweetID !== undefined) ? tweetID : null; | |
var response = service.fetch('https://api.twitter.com/1.1/statuses/update.json', { | |
method: 'post', | |
payload: { | |
status: tweetString, | |
in_reply_to_status_id: tweetID | |
} | |
}); | |
return JSON.parse(response); | |
} | |
//2バイト文字は2として文字数をカウント | |
function strLenJ(str) { | |
var len = 0; | |
str = escape(str); | |
for (var i = 0; i < str.length; i++, len++) { | |
if (str.charAt(i) == "%") { | |
if (str.charAt(++i) == "u") { | |
i += 3; | |
len++; | |
} | |
i++; | |
} | |
} | |
return len; | |
} | |
// 文字列 切り出し | |
function substr(text, len, truncation) { | |
if (truncation === undefined) { truncation = ''; } | |
var text_array = text.split(''); | |
var count = 0; | |
var str = ''; | |
for (i = 0; i < text_array.length; i++) { | |
var n = escape(text_array[i]); | |
if (n.length < 4) count++; | |
else count += 2; | |
if (count > len) { | |
return str + truncation; | |
} | |
str += text.charAt(i); | |
} | |
return text; | |
} | |
/* ====== 過去記事抽出など ====== */ | |
// 過去記事の投稿 | |
function postOldEntry() { | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
var sheet = ss.getSheetByName(TARGET_SHEET); | |
var lastRow = sheet.getLastRow(); | |
// スプレッドシートの更新とタイミングが重なった場合は何も投稿しない | |
if(lastRow == 0) return; | |
// スプレッドシートからデータ取得 | |
var publishDates = sheet.getRange("A1").offset(0, 0, lastRow).getValues(); | |
var updateDates = sheet.getRange("B1").offset(0, 0, lastRow).getValues(); | |
var titles = sheet.getRange("C1").offset(0, 0, lastRow).getValues(); | |
var urls = sheet.getRange("D1").offset(0, 0, lastRow).getValues(); | |
// ランダムに情報抽出 | |
var is_exclude = false; | |
while(is_exclude == false) { | |
var rand = Math.ceil(Math.random() * lastRow - 1); | |
var url = urls[rand][0]; | |
var ex_row = findRow(ss.getSheetByName(EXCLUDE_SHEET), url, 1); | |
if(ex_row === 0) { | |
is_exclude = true; | |
} | |
} | |
var publishedStr = publishDates[rand][0]; | |
var updatedStr = updateDates[rand][0]; | |
var title = titles[rand][0]; | |
// 日時フォーマット変更 | |
var published = Utilities.formatDate(new Date(publishedStr), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm'); | |
var updated = Utilities.formatDate(new Date(updatedStr), 'Asia/Tokyo', 'yyyy/MM/dd HH:mm'); | |
var tweetStr = ""; | |
tweetStr += "🤖<カコキジ!! "; | |
tweetStr += "#過去記事bot #はてなブログ \n"; | |
tweetStr += url + "\n"; | |
tweetStr += "🕘" + published + " 投稿\n"; | |
tweetStr += "🔃" + updated + " 更新\n"; | |
tweetStr += "📰" + title + "\n"; | |
// 文字数チェック(url は 23文字固定でカウント) | |
var tweetLength = strLenJ(tweetStr) - strLenJ(url) + 23; | |
if(tweetLength > 280) { | |
tweetStr = substr(tweetStr, 276 - 23 + strLenJ(url), "..."); | |
} | |
// 過去記事 投稿 | |
response = postUpdateStatus(tweetStr); | |
// 過去記事bot の作り方記事にリンクを貼る | |
if(PROMOTE_OK) { | |
var tweetID = response.id_str; | |
tweetStr = "#はてなブログ 向け #過去記事bot の導入方法はこちら\nhttps://twitter.com/stray_engineer/status/975298853061607424"; | |
response = postUpdateStatus(tweetStr, tweetID); | |
} | |
} | |
// スプレッドシート クリア | |
function clearSheet() { | |
var myActiveSpreadSheet = SpreadsheetApp.getActiveSpreadsheet(); | |
var myActiveSheet = myActiveSpreadSheet.getSheetByName(TARGET_SHEET); | |
myActiveSheet.clear(); | |
} | |
// スプレッドシートに記事一覧情報 入力 | |
function scraipingHatenaBlog() { | |
var myActiveSpreadSheet = SpreadsheetApp.getActiveSpreadsheet(); | |
var myActiveSheet = myActiveSpreadSheet.getSheetByName(TARGET_SHEET); | |
// 末尾が"/"の場合 削除 | |
if(BLOG_URL[BLOG_URL.length - 1] === "/") { BLOG_URL = BLOG_URL.slice(0, BLOG_URL.length - 1); } | |
// RSS 取得 | |
var nonce = getNonce(""); | |
var response = UrlFetchApp.fetch(BLOG_URL + "/feed?page=" + nonce); | |
// エントリー情報 抽出 | |
var myRegexp = /<entry>([\s\S]*?)<\/updated>/gi; | |
var match = response.getContentText().match(myRegexp); | |
// 過去記事を全て処理 | |
while(match != null) { | |
for(var i in match) { | |
// 投稿日, 更新日, タイトル, URL を入力 | |
var matchData = match[i]; | |
writeData(myActiveSheet, matchData); | |
// 2ページ目以降を処理するために nonce 更新 | |
var published = fetchString(matchData, "<published>", "<\/published>"); | |
nonce = getNonce(published); | |
} | |
// 2ページ目以降を取得 | |
response = UrlFetchApp.fetch(BLOG_URL + "/feed?page=" + nonce); | |
match = response.getContentText().match(myRegexp); | |
} | |
} | |
// Nonce 取得 | |
function getNonce(dateString) { | |
var date = new Date(); | |
if(dateString != "") { | |
date = new Date(dateString); | |
} | |
return Math.floor((date.getTime()/1000)).toString(); | |
} | |
// 文字列抽出 | |
function fetchString(str, pre, suf) { | |
var reg = new RegExp(pre + '.*?' + suf); | |
var data = str.match(reg)[0] | |
.replace(pre, '') | |
.replace(suf, ''); | |
return data; | |
} | |
// Spreadsheet 書き込み | |
function writeData(sheet, matchData) { | |
var published = fetchString(matchData, "<published>", "<\/published>"); | |
var updated = fetchString(matchData, "<updated>", "<\/updated>"); | |
var title = fetchString(matchData, "<title>", "<\/title>"); | |
var link = fetchString(matchData, "<link href=\"", "\"\/>"); | |
// https化されている場合、?utm_source=feed が付くので除去しておく | |
if(link.lastIndexOf('?') > 0) { | |
link = link.substring(0, link.lastIndexOf('?')); | |
} | |
var row = sheet.getLastRow() + 1 | |
sheet.getRange(row, 1).setValue(published); | |
sheet.getRange(row, 2).setValue(updated); | |
sheet.getRange(row, 3).setValue(title); | |
sheet.getRange(row, 4).setValue(link); | |
} | |
// シート内検索 | |
function findRow(sheet, val, col){ | |
if(sheet == null) return 0; | |
var dat = sheet.getDataRange().getValues(); | |
for(var i=1; i<dat.length; i++){ | |
if(dat[i][col-1] === val){ | |
return i+1; | |
} | |
} | |
return 0; | |
} | |
// --- 正確に時間指定したい人向け --- | |
// トリガー設定 | |
function setTrigger() { | |
var len = POST_TIMES.length; | |
for(var i=0; i<POST_TIMES.length; i++) { | |
var triggerDay = new Date(); | |
triggerDay.setHours(POST_TIMES[i].substr(0, 2)); | |
triggerDay.setMinutes(POST_TIMES[i].substr(2, 2)); | |
ScriptApp.newTrigger("postOldEntry").timeBased().at(triggerDay).create(); | |
} | |
} | |
// トリガー削除 | |
function deleteTrigger() { | |
var triggers = ScriptApp.getProjectTriggers(); | |
for(var i=0; i < triggers.length; i++) { | |
if (triggers[i].getHandlerFunction() == "postOldEntry") { | |
ScriptApp.deleteTrigger(triggers[i]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment