![見出し画像](https://assets.st-note.com/production/uploads/images/48096087/rectangle_large_type_2_4c6b4196b1333b55c4f36625cda2dc58.jpg?width=1200)
GASを使って #コンパス のデッキをランダムに生成するTwitterBotを作った話
手短に。
こんなのを作りました。
@Deck_Cps
目次
1.開発経緯
2.Bot用のTwitterアカウント作って色々申請
3.開発環境つくるよ
4.ソース書いた
5.トリガー引いて放置
6.保守作業めんどい
7.最後に
1.開発経緯
TwitterはBotが気楽に作れるSNSと聞いて遊び半分で作り始めた。
自分が好きなスマホゲーム #コンパス とTwitterのBotが組み合わさったら
面白そうと思い、「カードデッキをランダムで生成する」ものを思い付いた。
2.Bot用のTwitterアカウント作って色々申請
詳細は大幅に割愛します。
ここから始める方はこのサイトとか参考にしていただければ。
最近はTwitter Developer Portalの申請形式が変化しているので
何とか解読して、「Read Write」できるappを作成します。
3.開発環境つくるよ
そんなに大したことではないのですが、
1.Googleスプレッドシートから新規作成
2.「拡張機能」のApps Scriptをクリック
3.TwitterWebServiceというライブラリをV8で使える環境作る
4.ソース書いた
gitにも上げてますが、こちらにも上げておきます。
//コード.gs
// twitterエンドポイントURL
var tw_endpoint_statuses = "https://api.twitter.com/1.1/statuses/update.json";
var tw_enpoint_media = "https://upload.twitter.com/1.1/media/upload.json";
//認証用インスタンスの生成
var twitter = TwitterWebService.getInstance(
'xxxxxxxxxxxxxxxxxxxxxxxxx',//API Key
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'//API secret key
);
//アプリを連携認証する
function authorize() {
// @ts-ignore
twitter.authorize();
}
//認証を解除する
function reset() {
// @ts-ignore
twitter.reset();
}
//認証後のコールバック
function authCallback(request) {
// @ts-ignore
return twitter.authCallback(request);
}
// 乱数結果を取得する関数
function Deck() {
Logger.log("Deck_Start");
// 定義ファイルの取得
var spreadsheet = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
var sh = spreadsheet.getActiveSheet();
Logger.log("Loding_spreadsheet");
//スプレッドシートの指定
var sheet = spreadsheet.getSheetByName('hero');
//配列を定義
var deck = [];
var deckIds = [];
//実行フラグ関係
let Img_Twi_flag = false;
const image_tweet_interval = 12;
let Twicount = 0;
// 項目数を取得
let Tweetct = sheet.getRange("Q2").getValue();
const Heroct = sheet.getRange('B:B').getValues().filter(String).length - 1;
let Cardct = sheet.getRange('H:H').getValues().filter(String).length - 1;
let Imagect = sheet.getRange('I:I').getValues().filter(String).length - 1;
//項目数の内容をログに出力
Logger.log("SpreadSheet_getRange\nTweetct:"+Tweetct+"\nHeroct:"+Heroct+"\nCardct:"+Cardct+"\nImagect:"+Imagect);
// 出力する項目の一覧を取得する
var heroList = sheet.getRange(2, 2, Heroct, 1).getValues();
var cardList = sheet.getRange(2, 8, Cardct, 1).getValues();
var ImageList = sheet.getRange(2, 9, Imagect, 1).getValues();
Logger.log("SpreadSheet_getValues");
//ランダムな値を取得する
let Herorm = Math.floor(Math.random()*(Heroct));
let Cardrm = Math.floor(Math.random()*(Cardct));
if( Tweetct >= image_tweet_interval )
{
//画像付きツイート処理
Img_Twi_flag = true;
sh.getRange("Q2").setValue(1);
Logger.log("画像ツイートフラグは:"+Img_Twi_flag);
}
else
{
//文字のみのツイート処理
Img_Twi_flag = false;
// @ts-ignore
Twicount = Tweetct + 1;
sh.getRange("Q2").setValue(Twicount);
Logger.log("画像ツイートフラグは:"+Img_Twi_flag);
}
Logger.log("TweetCount:"+Tweetct);
Logger.log("Random_Deck_go")
//True:画像ツイ
//False:文字ツイ
if( Img_Twi_flag )
{
//4回繰り返す
for ( let i = 0; i < 4; i++) {
Cardrm = Math.floor(Math.random()*(Cardct));
Logger.log(i+":"+cardList[Cardrm]);
//deckというcardListインデックスに結果を代入する
//deckIdsというImageListインデックスにも結果を代入する
deck.push(cardList[Cardrm]);
deckIds.push(ImageList[Cardrm]);
//引いた番号がカードリストの最終番号ではないかどうか
if( Cardrm != Cardct ){
//結果番号のカード内容をカードリストの最終番目に格納する(実質の除外)(5枚のカードの3番目を引いた時、3番の内容を5番目の内容と交換し、合計カード数を-1以下略)
cardList[Cardrm] = cardList[Cardct-1];
ImageList[Cardrm] = ImageList[Imagect-1];
}
//選んだ結果がカード配列の最終番号であった場合、次選定する合計カード数を-1し除外する(5枚のカードの5番目を引いた時、次のランダム範囲は1~4番までにし5番目を除外する)
Cardct--;
Imagect--;
}
// 取得したカードを出力する関数へ引き渡す
Logger.log("Send_PostTweet");
postTweet_pic(heroList[Herorm],deck[0],deck[1],deck[2],deck[3],deckIds);
}
else
{
//4回繰り返す
for ( let i = 0; i < 4; i++) {
Cardrm = Math.floor(Math.random()*(Cardct));
//deckというcardListインデックスに結果を代入する
deck[i] = cardList[Cardrm];
Logger.log(i+":"+cardList[Cardrm]);
//引いた番号がカードリストの最終番号ではないかどうか
if( Cardrm != Cardct ){
//結果番号のカード内容をカードリストの最終番目に格納する(実質の除外)(5枚のカードの3番目を引いた時、3番の内容を5番目の内容と交換し、合計カード数を-1以下略)
cardList[Cardrm] = cardList[Cardct-1];
}
//選んだ結果がカード配列の最終番号であった場合、次選定する合計カード数を-1し除外する(5枚のカードの5番目を引いた時、次のランダム範囲は1~4番までにし5番目を除外する)
Cardct--;
}
// 取得したカードを出力する関数へ引き渡す
Logger.log("Send_PostTweet");
postTweet_txt(heroList[Herorm],deck[0],deck[1],deck[2],deck[3]);
}
}
// twitterへ文字ツイートを投稿
function postTweet_txt(hero,deck1,deck2,deck3,deck4) {
// @ts-ignore
var service = twitter.getService();
var endPointUrl = 'https://api.twitter.com/1.1/statuses/update.json';
var response = service.fetch(endPointUrl, {
method: 'post',
payload: {
status: '【' + hero + '】' + 'で\n' + '\n' + '・'+ deck1 + '\n' + '・'+ deck2 + '\n' + '・'+ deck3 + '\n' + '・'+ deck4 + '\n' + '\nを使ったデッキ'
}
});
}
// twitterへ画像ツイートを投稿
// 返り値はツイートした投稿のID
function postTweet_pic(hero,deck1,deck2,deck3,deck4,img_ids){
Logger.log("Start_PostTweet");
// @ts-ignore
var service = twitter.getService(); //TwitterWebServiceのインスタンス
// 順番にエンコード→アップロードする。*2
for(var i = 0; i < img_ids.length; i++){
Logger.log("uploding_media..."+i);
var img_blob = DriveApp.getFileById(img_ids[i]);
var img_64 = Utilities.base64Encode(img_blob.getBlob().getBytes());
var img_upload = service.fetch(
tw_enpoint_media, {
'method' : "POST",
'payload': { 'media_data': img_64 }
}
);
Logger.log("upload_success!!");
img_ids[i] = JSON.parse(img_upload).media_id_string;
}
// ツイート
Logger.log("Start_Tweet!");
var response = service.fetch(tw_endpoint_statuses, {
method: "post",
payload: {
'status' : '【' + hero + '】' + 'で\n' + '\n' + '・'+ deck1 + '\n' + '・'+ deck2 + '\n' + '・'+ deck3 + '\n' + '・'+ deck4 + '\n' + '\nを使ったデッキ',
'media_ids' : img_ids.join(',') // すでにアップロードした画像のid(カンマ区切りで結合) *3
}
});
tw_id = JSON.parse(response).id_str;
Logger.log("finish_Tweet");
Logger.log("やればできるやん!('ω')");
return tw_id;
}
Gitはこちら
https://github.com/Leeside18/Deck_CpsBot
5.トリガー引いて放置
あとは実行する関数を選択して好きなタイミングで実行。
ちなみに文字だけツイートか画像付きツイートかは
実行するたびにスプレッドシートの特定セルをカウントアップして更新し、その値を読むことで今回はどちらの処理なのかを分けています。
6.保守作業めんどい
なかなかにめんどいので色々と工夫はしました。
けどやっぱりめんどい。
1.カード、ヒーローの合計数は実行時に列ごと取得した配列からLengthとって乱数を設定した。
2.GoogleDriveに保存しているカード画像を自動で読み取りidをリスト化するツールを作成し、カード大量追加が来ても更新に時間がかからないようにした。
7.最後に
開発当初は配列がけっこうガバく、一度選ばれたカードをもう一度選んだら
再度選びなおしていたが、GASの処理時間の都合もあって、カードが重複してしまう問題とかもあった。その辺の解決策もソースに記載しているので
是非とも配列を扱う機会があれば参考にしてほしい。
キーワードは「除外」だった。
画像付きツイートについては環境をGASに置いていることもあって
GoogleDriveが一番適切だと考えた。これだと15GBまでしか置けないが、
画像が埋まるようならその時考える。
事前に格納したDriveから画像のidを取得しておき、スプレッドシートに「カード名」「画像id」を紐づける作業がめんどかった。けど
Twitterへのアップロード処理が何とか形になったのがこのBotづくりの収穫だった。
これを機にGAS極めたいなー