AtCoder頑張りたい
AtCoder頑張ってますか?私は頑張っていますがその努力は実っていません。
AtCoderはすばらしい競技プログラミングですが、開催が不定期なのが唯一の不満です。不定期だと次の予定がいつなのか忘れてしまうんです。でも毎回問題を考えるのはとても大変でしょうから、しょうがないですよね。
こちらの努力でなんとかしましょう。具体的にはAtCoderの開催予定はTwitterで定型文を使って告知されるので、ここから情報収集して勝手にGoogleカレンダーに登録すればよさそうです。
調べたらTwitterで検索した結果を自動でLINEに投稿している方がいました。
定期的に Twitter で特定の内容を検索して通知してくれる Bot を作った話(前編)
なるほどるほど。Google Apps Scriptを使うと実現できるみたいです。あとは以下のようにカスタマイズすればよさそうですね。
- 検索は特定のアカウント(@atcoder)のみを対象にする
- 検索ワードは"AtCoder Beginner Contest"と"【コンテスト開催のお知らせ】"とする(ABC以外無理だから)
- 検索期間は7日(ツイッターAPIのデフォルト)
- みつかったツイートの日時とABCの回を正規表現を利用して取得し、それがGoogleカレンダーに既に登録されていなければ登録する(開始時間は21時で固定とする。面倒なので)
- 以上を1日1回実行する
そういうことで書いていきます。前提として、Twitter APIの各種キーの取得と、CalendarApp.getDefaultCalendar()で自分の登録したいGoogleカレンダーがgetできるようにしておきます。
function getToken() {
const apiKey = "XXX";
const apiSecretKey = "XXX";
const credential = Utilities.base64Encode(apiKey + ':' + apiSecretKey);
const options = {
"method": "post",
"contentType": "application/x-www-form-urlencoded;charset=UTF-8",
"headers": { "Authorization" : "Basic " + credential },
"payload": { "grant_type": "client_credentials" }
};
const uri = "https://api.twitter.com/oauth2/token";
const res = JSON.parse(UrlFetchApp.fetch(uri, options));
return res.access_token;
}
function searchTwitter(){
const options = {
"method": "get",
"headers": { "Authorization" : "Bearer " + getToken()}
}
const q = encodeURIComponent('from:atcoder "AtCoder Beginner Contest" "【コンテスト開催のお知らせ】"');
const uri = "https://api.twitter.com/1.1/search/tweets.json?q=" + q + "&result_type=recent&tweet_mode=extended";
const res = JSON.parse(UrlFetchApp.fetch(uri, options));
var texts = [];
res.statuses.forEach(function(status) {
texts.push(status.full_text);
});
return texts;
}
function extractTwitter(text){
const abc_date = text.match(/[0-9]{4}-[0-9]{2}-[0-9]{2}/gi)[0];
const abc_name = text.match(/AtCoder Beginner Contest [0-9]{3}/gi)[0];
const abc_year = abc_date.split("-")[0];
const abc_month = abc_date.split("-")[1];
const abc_day = abc_date.split("-")[2];
const abc_data = {
"name": abc_name,
"year":abc_year,
"month":abc_month,
"day":abc_day};
return abc_data;
}
function check_calendar(abc_data, calendar, startTime) {
var events = calendar.getEventsForDay(startTime);
var titlelist = events.map(value => value.getTitle());
var ans = titlelist.some(value => value==abc_data.name);
return ans
}
function regist_to_calendar(abc_data) {
if(Object.keys(abc_data).length){
const calendar = CalendarApp.getDefaultCalendar();
const title = abc_data.name;
const startTime = new Date(abc_data.year, abc_data.month-1, abc_data.day, 21, 0, 0);
const endTime = new Date(abc_data.year, abc_data.month-1, abc_data.day, 22, 0, 0);
// monthは0-index
if (!check_calendar(abc_data, calendar, startTime)){
calendar.createEvent(title, startTime, endTime);
}
}
}
function main(){
getToken()
const texts = searchTwitter()
if (texts.length) {
texts.map(function(text){
abc_data = extractTwitter(text);
regist_to_calendar(abc_data);
})
}
}
このままだとタイムゾーンがずれていてめちゃくちゃになってしまうので、プロジェクトの設定から『「appsscript.json」マニフェスト ファイルをエディタで表示する』にチェックを入れて、現れたappsscript.jsonを以下のように変更します(参考:Google Apps Script タイムゾーンの設定
)。
{
"timeZone": "Asia/Tokyo",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8"
}
あとはmain関数をトリガーで実行します。
Twitterに投稿があった翌日、こんな感じでイベントが登録されました。
できた!通知もデフォルトでつきます。
私は無事ゲームに集中しすぎて作った翌週の回に参加し忘れましたが、これで皆さんはAtCoderを忘れることはありませんね。
Header photo by Estée Janssens on Unsplash