【なんでも自動化してみたい🤖1.1】 Twitter ✖️ note ✖️ GAS (Google App Script)
前回
前回の内容はタイトル通りTwitter API + Note RSS + Heroku Freeで実現しましたが、
Herokuは無料枠がweb+botではすぐなくなってしまうと思います。。。。
なので今回はGASによるTwitterのBotを作成していこうと思います。。。
GASでも定期実行はできる!!!!!!はず😅
GASについて👍♻︎
下記の記事を読んで GASを理解しましょう!
基本的にJavaScriptの文法が解れば爆速でできるようになると思います!🚀
Twitter のラッパークラス🕊
「authCallback」をどうしていいかわからず、、、
少し汚いClassになってしまいました。(改良したい)🙇♂️
できるだけTwitterのAPIの変更が波及しないように型を変更してレスポンスしています。
const config = {};
function authCallback(request) {
const service = TwitterDataSource.getAuthService(config.API_KEY, config.API_SECRET, config.TOKEN, config.TOKEN_SECRET);
return service.handleCallback(request)
? HtmlService.createHtmlOutput('成功')
: HtmlService.createHtmlOutput('失敗');
}
class TwitterDataSource {
static getAuthService(apiKey, apiSecret, token, tokenSecret) {
return OAuth1.createService('Twi')
.setConsumerKey(apiKey)
.setConsumerSecret(apiSecret)
.setAccessToken(token, tokenSecret)
.setAccessTokenUrl()
.setRequestTokenUrl('https://api.twitter.com/oauth/access_token')
.setAuthorizationUrl('https://api.twitter.com/oauth/authorize')
.setCallbackFunction(authCallback.name);
}
constructor(apiKey, apiSecret, token, tokenSecret) {
this.basePath = "https://api.twitter.com/1.1";
// 直したい
config.API_KEY = apiKey
config.API_SECRET = apiSecret
config.TOKEN = token
config.TOKEN_SECRET = tokenSecret
this.service = TwitterDataSource.getAuthService(config.API_KEY, config.API_SECRET, config.TOKEN, config.TOKEN_SECRET);
}
retryOneFetch(path, method, payload, retry) {
if (this.service.hasAccess()) {
Logger.log(`Request -> Path: ${path}\nMethod: ${method}\nPayload: ${payload}`);
try {
const response = this.service.fetch(path, { method, payload });
Logger.log(`Response -> Path: ${path}\nMethod: ${method}\nCode: ${response.getResponseCode()}\nHeader: ${JSON.stringify(response.getHeaders())}`);
return JSON.parse(response.getContentText());
} catch (e) {
console.error(e);
}
} else if (retry) {
this.service = TwitterDataSource.getAuthService(config.API_KEY, config.API_SECRET, config.TOKEN, config.TOKEN_SECRET);
this.retryOneFetch(path, method, payload, false);
}
}
post(textString) {
Logger.log(`post: ${textString}`);
const path = `${this.basePath}/statuses/update.json`;
const body = { status: textString };
const method = 'post';
this.retryOneFetch(path, method, body, true);
}
follow(nameString) {
Logger.log(`follow: ${nameString}`);
const path = `${this.basePath}/friendships/create.json?screen_name=${nameString}`;
const method = "post";
this.retryOneFetch(path, method, undefined, true);
}
unfollow(nameString) {
Logger.log(`unfollow: ${nameString}`);
const path = `${this.basePath}/friendships/destroy.json?screen_name=${nameString}`;
const method = "post";
this.retryOneFetch(path, method, undefined, true);
}
seacrhPosts(textString, count) {
Logger.log(`seacrhPosts: ${textString}`);
const path = `${this.basePath}/search/tweets.json?q=${textString}&result_type=mixed&locale=ja&count=${count ? count : 10}`;
const method = "get";
const response = this.retryOneFetch(path, method, undefined, true);
const convert = (status) => ({
id : status.user.screen_name,
text : status.text,
});
return response ? response.statuses.map(convert) : undefined;
}
getMyAccountId() {
const path = `${this.basePath}/account/settings.json`;
const method = "get";
const response = this.retryOneFetch(path, method, undefined, true);
return response ? response.screen_name : undefined;
}
getFriends(id, count) {
const path = `${this.basePath}/friends/list.json?screen_name=${id}&count=${count ? count : 10}`;
const method = "get";
const response = this.retryOneFetch(path, method, undefined, true);
const convert = (user) => ({ id : user.screen_name });
return response ? response.users.map(convert) : undefined;
}
getFollowers(id, count) {
const path = `${this.basePath}/followers/list.json?screen_name=${id}&count=${count ? count : 10}`;
const method = "get";
const response = this.retryOneFetch(path, method, undefined, true);
const convert = (user) => ({ id : user.screen_name });
return response ? response.users.map(convert) : undefined;
}
}
noteの記事をRSSで取得📡
class NoteDatasource {
constructor(urlString) {
this.myNoteRssUrl = urlString;
}
getArticles() {
const data = UrlFetchApp.fetch(this.myNoteRssUrl);
const xml = XmlService.parse(data.getContentText());
const entries = xml.getRootElement().getChildren('channel')[0].getChildren('item');
return entries.map(entry => ({
title : entry.getChildText("title"),
link : entry.getChildText("link"),
}));
}
}
実行してみる🏃♀️
// ケースをまとめておく
const UseCase = {
textOfTweetRss : `
#駆け出しエンジニア
#駆け出しエンジニアと繋がりたい
#プログラミング
#プログラミング初心者
`,
tweetRss: (twitter, note) => {
const articles = note.getArticles();
const article = articles[Math.floor(Math.random() * articles.length)]; //ランダムに記事を一つ選ぶ
twitter.post(`${article.title}\n${article.link}` + UseCase.textOfTweetRss);
},
}
// メイン処理
function main() {
const API_KEY = 'あなたの Twitter API Key';
const API_SECRET = 'あなたの Twitter API Secret';
const TOKEN = 'あなたの Twitter Token';
const TOKEN_SECRET = 'あなたの Twitter Token Secret';
const NOTE_URL = 'あなたの note URL';
const twitter = new TwitterDataSource(API_KEY, API_SECRET, TOKEN, TOKEN_SECRET);
const note = new NoteDatasource(NOTE_URL);
UseCase.tweetRss(twitter, note);
}
よし、定期実行出来るようにして行こう!?
実行する関数を選択
時間の間隔を選択
基本的に上記の2点を設定しておけばOKですかね?
まとめ✅
今回は前回の移植なのでそこまで深く書いません。。🙇♂️
ですが、TwitterでGASという選択肢がどこかの誰かに認知されることを祈っています。✨
次回はDeZeroの続きで会いましょう👋