見出し画像

【なんでも自動化してみたい🤖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, undefinedtrue);
  } 

  unfollow(nameString) {
    Logger.log(`unfollow: ${nameString}`);
    const path = `${this.basePath}/friendships/destroy.json?screen_name=${nameString}`;
    const method = "post";
    this.retryOneFetch(path, method, undefinedtrue);
  } 

  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, undefinedtrue);
    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, undefinedtrue);
    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, undefinedtrue);
    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, undefinedtrue);
    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の続きで会いましょう👋

この記事が気に入ったらサポートをしてみませんか?