Skip to content

Instantly share code, notes, and snippets.

@dolpen
Created September 8, 2017 02:17
Show Gist options
  • Save dolpen/8c8c6612cdc0d987c29b7f0c3be2f138 to your computer and use it in GitHub Desktop.
Save dolpen/8c8c6612cdc0d987c29b7f0c3be2f138 to your computer and use it in GitHub Desktop.
// 依存している外部ライブラリ(リソース > ライブラリから追加)
// DiscordWebhook(1) : 1QyXmzA-4hugnTy3Nnvbrl0qsSOe4kmStcErJCgtVewfdzlftB0vDUJ-E
// SpreadSheetUtils(14) : 1NHmUM9XoGSiIS4UsBMQY0-pFheGsZ-SPCbMIjJ6RPhHGxZYNa2uQcz4c
// Twitter(1) : 1M7klEgU3suAbv1S0LUm3oamiI45DYRxSujnYee9qJMIaQ6lWRzjyRI9b
var settings = {
// oauth_* : https://dev.twitter.com/ 見て作る
oauth_consumer_token: '********',
oauth_consumer_secret: '********',
oauth_access_token: '********',
oauth_access_secret: '********',
target_screen_name: 'gesotown_shop', // ゲソタウン情報bot
read_count: 20, // ツイート収集単位<=200
translation_sheet: 'your_spreadsheet_id', //対訳表ID
translation_stages: 'stages', //ルール/ステージ対訳表シート名
translation_gps: 'gps', //ギアパワー対訳表シート名
discord_id: 'id_for_prod', // discordのbot id
discord_token: 'id_for_prod', // discordのbot token
};
var utils = {
time : function(time){
var d = new Date(time);
return '' + ('0' + d.getHours()).slice(-2) + ':'
+ ('0' + d.getMinutes()).slice(-2);
},
datetime : function(time){
var d = new Date(time);
return '' + d.getFullYear() + '/'
+ ('0' + (d.getMonth() + 1)).slice(-2) + '/'
+ ('0' + d.getDate()).slice(-2) + ' '
+ ('0' + d.getHours()).slice(-2) + ':'
+ ('0' + d.getMinutes()).slice(-2);
}
};
// スプレッドシートに[英名,日本名,略名]の対訳表を作っておく
var Translation = function(s,p){
var d = {};
SpreadSheetUtils.asWrapper(s,p).getAllRows().forEach(function(row){d[row[0]] = {name:row[1],short:row[2]}});
this.dict = d;
};
Translation.prototype = {
toShort : function(k){
if(this.dict[k])return this.dict[k].short;
return '"'+k+'?"';
}
};
// マッチルール/ステージ情報を取得する
var info = (function(){
var translation = new Translation(settings.translation_sheet,settings.translation_stages);
var endpoint = 'https://splatoon2.ink/data/schedules.json';
var CityInfo = function(){
this.schedule = null;
this.cached = null;
this.now = new Date();
};
CityInfo.prototype = {
_fetch: function(){
if (this.schedule != null)return this.schedule;
var resp = UrlFetchApp.fetch(endpoint, { 'method': 'get' });
var data = JSON.parse(resp.getContentText());
this.schedule = data;
return data;
},
_get: function(mode, time){
return this._fetch()[mode].filter(function(info){
return info.start_time <= time && info.end_time >= time;
})[0];
},
_canonical: function(name){
return translation.toShort(name);
},
_date: function(time){
return utils.datetime(time);
},
_period: function(time){
var r = this._get('regular',time);
var g = this._get('gachi',time);
var l = this._get('league',time);
return '['
+ this._date(r.start_time*1000)
+ '〜]\nレ: '
+ this._canonical(r.stage_a.name)
+ this._canonical(r.stage_b.name)
+ '\nガ: '
+ this._canonical(g.stage_a.name)
+ this._canonical(g.stage_b.name)
+ this._canonical(g.rule.name)
+ '\nリ: '
+ this._canonical(l.stage_a.name)
+ this._canonical(l.stage_b.name)
+ this._canonical(l.rule.name)
+ '\n';
},
_toMessage: function(){
var message = '';
message += this._period((this.now.getTime())/1000);
message += '\n' + this._period((this.now.getTime())/1000+3600*2);
message += '\n' + this._period((this.now.getTime())/1000+3600*4);
return message;
},
get: function(){
if (this.cached != null)return this.cached;
var message = '';
var info = this._fetch();
message += info ? this._toMessage() : 'ニュースの取得に失敗しました';
this.cached = message;
return message;
}
};
return new CityInfo();
})();
// 直近のサーモンラン予定の取得
var salmon = (function(){
var endpoint = 'https://splatoon2.ink/data/salmonruncalendar.json';
var SalmonInfo = function(){
this.schedule = null;
this.cached = null;
this.now = new Date();
};
SalmonInfo.prototype = {
_fetch: function(){
if (this.schedule != null)return this.schedule;
var resp = UrlFetchApp.fetch(endpoint, { 'method': 'get' });
var data = JSON.parse(resp.getContentText());
this.schedule = data.schedules;
return data.schedules;
},
_getNearest: function(time){
return this._fetch().filter(function(info){
return info.end_time >= time;
}).sort(function(a,b){
return a.start_time - b.start_time
})[0];
},
_date: function(time){
return utils.datetime(time);
},
_period: function(time){
var r = this._getNearest(time);
var started = time - r.start_time >= 0;
return 'サ: ' + (started?'**【募集中】** ':'準備中 ')
+ this._date(r.start_time*1000) + '〜' + this._date(r.end_time*1000)
},
_toMessage: function(){
return this._period((this.now.getTime())/1000);
},
get: function(){
if (this.cached != null)return this.cached;
var message = '';
var info = this._fetch();
message += info ? this._toMessage() : 'バイト情報の取得に失敗しました';
this.cached = message;
return message;
}
};
return new SalmonInfo();
})();
// ゲソタウン新着お知らせbotのツイートを解析して有効なギアの情報を得る
var gear = (function(){
var translation = new Translation(settings.translation_sheet,settings.translation_gps);
var GearInfo = function(){
this.tweets = null;
this.cached = null;
this.now = new Date();
};
GearInfo.prototype = {
_fetch: function(){
if (this.tweets != null)return this.tweets;
var self = this;
var twitter = Twitter.newInstance(settings.oauth_consumer_token, settings.oauth_consumer_secret, settings.oauth_access_token, settings.oauth_access_secret);
var url = 'https://api.twitter.com/1.1/statuses/user_timeline.json?trim_user=1&screen_name='
+ settings.target_screen_name
+ '&count='
+ settings.read_count;
var resp = twitter.execRequest(url, 'get', {});
this.tweets = resp.map(function(tweet){
return {
w : tweet.text.replace(/(/g,'(').replace(/)/g,')').match(/^([^\(]+)\([^:]+:([^\)]+\)?)\)[^:]+:([^\n]+)[^:]+:([^\(]+)\(([^\)]+\)?)\)/),
c : tweet.created_at
}
}).filter(function(o){return o.w !== null;}).map(function(o){
var tm = new Date(o.c).getTime();
var lt = tm%(1000*3600*2);
var w =o.w;
[2,3,5].forEach(function(i){w[i] = self._canonical(w[i]);});
return {
timestamp : tm - lt + 1000*3600*12, // expires
brand : w[4]+'('+w[5]+')',
item : w[1]+'('+w[2]+' → '+w[3]+')',
name : w[1],
spec : w[2]+'+'+w[5]+' → **'+w[3]+'+'+w[5]+'**',
isXxx : w[5] == w[3] // bad code!!
};
});
return this.tweets;
},
_get: function(now){
return this._fetch().filter(function(data){
return data.timestamp >= now;
}).sort(function(a,b){
return a.timestamp - b.timestamp;
});
},
_date: function(time){
return utils.time(time);
},
_canonical: function(name){
return translation.toShort(name);
},
_toMessage: function(){
var message = 'ゲ:\n';
var self = this;
message += this._get((new Date()).getTime()).map(function(g){
return '[〜' + self._date(g.timestamp) + '] ' + (g.isXxx?'**【違法】**':'') + '\n    ' + g.name  + ' ( ' + g.spec+' )';
}).join('\n');
return message;
},
get: function(){
//if (this.cached != null)return this.cached;
var message = '';
var info = this._fetch();
message += info ? this._toMessage() : 'ゲソタウン情報の取得に失敗しました';
this.cached = message;
return message;
}
};
return new GearInfo();
})();
// cron で curl なりすると動くエンドポイント
function doGet(){
var discord = DiscordWebhook.newInstance(settings.discord_id, settings.discord_token);
var news = '【ハイカラニュース】\n\n' + info.get() + '\n' + salmon.get() + '\n\n' + gear.get();
discord.send(news);
return ContentService.createTextOutput("{}").setMimeType(ContentService.MimeType.JSON);
}
// 検証用
function doTest(){
var discord = DiscordWebhook.newInstance('id_for_test','token_for_test');
var news = '【ハイカラニュース】\n\n' + info.get() + '\n' + salmon.get() + '\n\n' + gear.get();
Logger.log(news);
discord.send(news);
return ContentService.createTextOutput("{}").setMimeType(ContentService.MimeType.JSON);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment