DTPab

DTPにまつわるあれこれ

Googleシートに用意した内容をGASで自動ツイートしたい

まずは宣伝

今月発売予定の『+Designing』にて、自動化に関するリレーコラム「ラクラボ」の第二回を執筆しています。前回のラクラボは鰯屋の村上さんによるキーボードマエストロに関する内容でした。今回の僕の記事は、スクリプトに関する記事です。
といっても、普段ここに書いている内容と違い、スクリプトのインストール方法とか、そういう基本的な部分を書かせてもらっています。
僕がこのDTPという仕事を始めて(小さな制作会社にDTP未経験で2010年に入社)2年くらい、スクリプトというものを知りませんでした。知らないと調べようがない。だから『+Designing』のようなDTPに特化した書籍に、スクリプトというものがあるよ、こうやって使うんだよ、こんなに便利だよ、という情報が載ることに意味があると思っています。
そんなわけで現在がんばっておりますので、『+Designing』vol.46をよろしくお願いします!

本題

僕は基本的に、InDesignスクリプト開発に関する内容をメインに、DTPにまつわる内容をブログ記事にしたためています。
ブログに蓄積することで、Twitterのようにタイムラインを流れていってしまうことなく、検索性を高めつつ知識をストックできればいいなと思って始めたわけです。
ブログは自発的に調べるぶんにはいいのですが、ブログに「あの情報があったはず…」というのを知っていないと、わざわざ過去の記事をさかのぼったり、検索したりというところに至りません。要するに時間が経った記事はブログでも(Twitterほどではないにせよ)埋もれていくのですね。

そこで、過去のブログ記事を自動でツイートしたいな、と思いました。今回はそれをGoogleAppsScriptで実装してみました。
それにあたり、コミックマーケット94で頒布された「高尾技研」さんの『らぁらちゃんがGoogle Apps ScriptでTwitterのBotを作る本*1を応用することにしました。
このブログ記事は、同書の第2章、GoogleAppsScriptにTwitter用ライブラリを読み込んだことを前提に書いていきます。この本の中核部分が第2章なので、ぜひぜひお買い求めください!(現在在庫切れのようですので、PDF販売をお待ち下さい…)

やりたいこと

Googleスプレッドシートに用意したツイート内容を、数時間おきにランダムでツイートしたい。

かかった時間

スプレッドシートの内容を読み込んで、抽出して、ツイートするコードの作成まで1時間程度でした。その後、ツイート内容をシートに用意するのに1時間くらいかかりました。要するに、超・簡・単!

ツイートする内容の用意

スプレッドシートに、ツイートの分類、ツイート内容、という順で以下のようにツイート内容を用意しました。

f:id:uske_S:20180902085949p:plain

追々、もくもく会の告知やその他宣伝などもツイートしたいと思っているので、このようにツイート内容を区別できるような形にしました。
このスプレッドシートのIDを控えておいてください。IDは、スプレッドシートのアドレス、https://docs.google.com/spreadsheets/d/[スプレッドシートID]/edit#gid=0 の[スプレッドシートID]の部分です。

コード

先述のとおり、前掲書から「Twitterアプリケーションの登録」と「Twitter用ライブラリをインポート」を終えた状態とします。アクセストークンのアクセス権が「Read and Write」になっているかは確認してください。僕はこれで5分くらい悩みましたw

コードの解説

スクリプトは3つの関数で構成しました。

  • 指定した分類の内容をツイートする
  • スプレッドシートのツイート内容を抽出し、ランダムにひとつ返す
  • Twitter用ライブラリを利用したツイートする関数(なかひこさんの同人誌の内容まま)

順に見ていきます。

指定した分類の内容をツイートする

function tweetBlogPost(){
  DoTweet(getTweetContents("type A")); //次の関数のtweets変数のキーから選ぶ
}

この関数に実行トリガーを与え、定期的に自動ツイートをする仕組みにしました。
"type A"としていますが、先の画像の通り、僕の用意したスプレッドシートではA列に「記事紹介」と記入しているので、関数の引数は実際には"記事紹介"です。

スプレッドシートのツイート内容を抽出し、ランダムにひとつ返す

function getTweetContents(typeName) {
  var mySS = SpreadsheetApp.openById("Your SpreadSheet ID").getSheets()[0];
  var myDR = mySS.getDataRange();
  var myVL = myDR.getValues();
  //A列の内容をキーとしたオブジェクトを用意し、値に空の配列をセットしておく
  var tweets = {
    "type A": [],
    "type B": [], 
    "type C": []
  };
  //乱数の設定
  var randNum = Math.floor(Math.random() * Math.floor(myVL.length));
  //スプレッドシートの内容を読み込んで、tweets変数に用意したキーに配列としてpushしていく
  myVL.forEach(function (cv, i){
    //1列目は無視したいときは、インデックスが0のとき除外
    if (i === 0) {
      return;
    }
    //getValuesメソッドはスプレッドシートの行単位の[[A列],[B列]…]という二重配列になる
    //したがってcv[0]はA列の内容、cv[1]がB列の内容になる
    tweets[cv[0]].push("【"+cv[0]+"】"+cv[1]);
  });
  return tweets[typeName][randNum]; //読み込んだ内容から乱数で選ばれたものだけを返す
}

まず、ツイート内容を記述してあるスプレッドシートのIDを、mySS変数の"Your SpreadSheet ID"にセットしてください。
getSheets()メソッドでスプレッドシートにある各シートを取得しますが、このスクリプトでは最初のシートとしています。そうでない場合は([0])の部分を何番目のシートなのかインデックスを指定するか、getSheetsByNameメソッドを使ってシート名から取得するなどしてください。
詳しくは以下を参照。

Class Spreadsheet  |  Apps Script  |  Google Developers

あとはDataRange、Valuesを取得して、というところはほぼ定型句です。

続いて、tweets変数を定義します。

var tweets = {
  "type A": [],
  "type B": [], 
  "type C": []
};

この内容は仮なので、A列に用意したツイートの分類をオブジェクトキーとして設定します。
僕の場合は、「記事紹介」や「告知」などを用意しているので

var tweets = {
  "記事紹介": [],
  "告知": [], 
  "その他": []
};

としています。

var randNum = Math.floor(Math.random() * Math.floor(myVL.length));

整数の乱数を取得します。詳しくはMDNなどを参照のこと。

Math.random() - JavaScript | MDN

続いて、スプレッドシートから取得した情報を、A列の内容に合わせて篩いにかけます。

//スプレッドシートの内容を読み込んで、tweets変数に用意したキーに配列としてpushしていく
myVL.forEach(function (cv, i){
  //1列目は無視したいときは、インデックスが0のとき除外
  if (i === 0) {
    return;
  }
  //getValuesメソッドはスプレッドシートの行単位の[[A列],[B列]…]という二重配列になる
  //したがってcv[0]はA列の内容、cv[1]がB列の内容になる
  tweets[cv[0]].push("【"+cv[0]+"】"+cv[1]);
});

GASはES5なので、Array.forEachが使えます。使い方はやっぱりMDNあたりをどうぞ。

Array.prototype.forEach() - JavaScript | MDN

先頭の行にはヘッダ情報を記入してあるので、最初の行は省いています。そのためにi === 0のときは除外しています。
そういうのが必要なければこのif文は不要です。

さて、Range.getValuesメソッドでは行と列の内容が入れ子の二重配列として取得されます。
なのでforEachメソッドのcurrentValue(仮引数cv)はすべて単純な配列になります。インデックス0がA列、1がB列…ということです。
A列の内容をtweetsオブジェクトのキーとし、B列の内容をその値としてpushします。

return tweets[typeName][randNum]; //読み込んだ内容から乱数で選ばれたものだけを返す

その上で、先程取得した乱数のものを関数の戻り値として返します。

Twitter用ライブラリを利用したツイートする関数

function DoTweet(contents) {
  const auth_info = {
    "c_key":"Your Consumer Key",
    "c_sec":"Your Consumer Secret",
    "a_tok":"Your Access Token",
    "a_sec":"Your Access Token Secret",
  }
  Twitter.tweet(auth_info, contents);
}

ここはなかひこさんの同人誌の内容ほとんどそのままです。詳しくは同書を参照してください。
変えているのは、ツイート内容を引数として渡せるようにした部分です。

以上がコードの説明です。

定期的に動作させるトリガーを設定する

GASのスクリプトエディタの左上のアイコン群を見てください。

f:id:uske_S:20180902100536p:plain

この時計のようなアイコンをクリックします。

f:id:uske_S:20180902100825p:plain

このように設定したら完成です。

完成!

こんな感じで4時間ごとにツイートします。

今回は記事紹介ツイートだけですが、先頭の関数を複製し、同じようにして告知ツイートなどもできるようにしていこうと思ってます。
関数を分けてトリガーを設定することで、異なる頻度でツイートできるようになるので、お試しください。

*1:著者のなかひこさん(@takanakahiko)からお声がけいただき、制作のお手伝い、InDesign作業のバックアップをさせていただいた同人誌です。赤字確定の値段で販売されているので、ぜひBOOTHからブースト購入(上乗せ購入)してあげてください!