tuxbot-bot/cogs/utils/maps.py
2017-07-05 13:58:06 -04:00

148 lines
5.3 KiB
Python
Executable file

#!/bin/env python
# With credit to DanielKO
from lxml import etree
import datetime, re
import asyncio, aiohttp
NINTENDO_LOGIN_PAGE = "https://id.nintendo.net/oauth/authorize"
SPLATNET_CALLBACK_URL = "https://splatoon.nintendo.net/users/auth/nintendo/callback"
SPLATNET_CLIENT_ID = "12af3d0a3a1f441eb900411bb50a835a"
SPLATNET_SCHEDULE_URL = "https://splatoon.nintendo.net/schedule"
class Rotation(object):
def __init__(self):
self.start = None
self.end = None
self.turf_maps = []
self.ranked_mode = None
self.ranked_maps = []
@property
def is_over(self):
return self.end < datetime.datetime.utcnow()
def __str__(self):
now = datetime.datetime.utcnow()
prefix = ''
if self.start > now:
minutes_delta = int((self.start - now) / datetime.timedelta(minutes=1))
hours = int(minutes_delta / 60)
minutes = minutes_delta % 60
prefix = '**In {0} hours and {1} minutes**:\n'.format(hours, minutes)
else:
prefix = '**Current Rotation**:\n'
fmt = 'Turf War is {0[0]} and {0[1]}\n{1} is {2[0]} and {2[1]}'
return prefix + fmt.format(self.turf_maps, self.ranked_mode, self.ranked_maps)
# based on https://github.com/Wiwiweb/SakuraiBot/blob/master/src/sakuraibot.py
async def get_new_splatnet_cookie(username, password):
parameters = {'client_id': SPLATNET_CLIENT_ID,
'response_type': 'code',
'redirect_uri': SPLATNET_CALLBACK_URL,
'username': username,
'password': password}
async with aiohttp.post(NINTENDO_LOGIN_PAGE, data=parameters) as response:
cookie = response.history[-1].cookies.get('_wag_session')
if cookie is None:
print(req)
raise Exception("Couldn't retrieve cookie")
return cookie
def parse_splatnet_time(timestr):
# time is given as "MM/DD at H:MM [p|a].m. (PDT|PST)"
# there is a case where it goes over the year, e.g. 12/31 at ... and then 1/1 at ...
# this case is kind of weird though and is currently unexpected
# it could even end up being e.g. 12/31/2015 ... and then 1/1/2016 ...
# we'll never know
regex = r'(?P<month>\d+)\/(?P<day>\d+)\s*at\s*(?P<hour>\d+)\:(?P<minutes>\d+)\s*(?P<p>a\.m\.|p\.m\.)\s*\((?P<tz>.+)\)'
m = re.match(regex, timestr.strip())
if m is None:
raise RuntimeError('Apparently the timestamp "{}" does not match the regex.'.format(timestr))
matches = m.groupdict()
tz = matches['tz'].strip().upper()
offset = None
if tz == 'PDT':
# EDT is UTC - 4, PDT is UTC - 7, so you need +7 to make it UTC
offset = +7
elif tz == 'PST':
# EST is UTC - 5, PST is UTC - 8, so you need +8 to make it UTC
offset = +8
else:
raise RuntimeError('Unknown timezone found: {}'.format(tz))
pm = matches['p'].replace('.', '') # a.m. -> am
current_time = datetime.datetime.utcnow()
# Kind of hacky.
fmt = "{2}/{0[month]}/{0[day]} {0[hour]}:{0[minutes]} {1}".format(matches, pm, current_time.year)
splatoon_time = datetime.datetime.strptime(fmt, '%Y/%m/%d %I:%M %p') + datetime.timedelta(hours=offset)
# check for new year
if current_time.month == 12 and splatoon_time.month == 1:
splatoon_time.replace(current_time.year + 1)
return splatoon_time
async def get_splatnet_schedule(splatnet_cookie):
cookies = {'_wag_session': splatnet_cookie}
"""
This is repeated 3 times:
<span class"stage-schedule"> ... </span> <--- figure out how to parse this
<div class="stage-list">
<div class="match-type">
<span class="icon-regular-match"></span> <--- turf war
</div>
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
<div class="stage-list">
<div class="match-type">
<span class="icon-earnest-match"></span> <--- ranked
</div>
... <span class="rule-description"> ... </span> <--- Splat Zones, Rainmaker, Tower Control
... <span class="map-name"> ... </span>
... <span class="map-name"> ... </span>
</div>
"""
schedule = []
async with aiohttp.get(SPLATNET_SCHEDULE_URL, cookies=cookies, data={'locale':"en"}) as response:
text = await response.text()
root = etree.fromstring(text, etree.HTMLParser())
stage_schedule_nodes = root.xpath("//*[@class='stage-schedule']")
stage_list_nodes = root.xpath("//*[@class='stage-list']")
if len(stage_schedule_nodes)*2 != len(stage_list_nodes):
raise RuntimeError("SplatNet changed, need to update the parsing!")
for sched_node in stage_schedule_nodes:
r = Rotation()
start_time, end_time = sched_node.text.split("~")
r.start = parse_splatnet_time(start_time)
r.end = parse_splatnet_time(end_time)
tw_list_node = stage_list_nodes.pop(0)
r.turf_maps = tw_list_node.xpath(".//*[@class='map-name']/text()")
ranked_list_node = stage_list_nodes.pop(0)
r.ranked_maps = ranked_list_node.xpath(".//*[@class='map-name']/text()")
r.ranked_mode = ranked_list_node.xpath(".//*[@class='rule-description']/text()")[0]
schedule.append(r)
return schedule