2023-02-11 16:57:09 +00:00
|
|
|
import datetime
|
|
|
|
import dateutil
|
2023-02-11 14:55:39 +00:00
|
|
|
import logging
|
|
|
|
import json
|
2023-02-11 16:57:09 +00:00
|
|
|
import re
|
2023-02-11 14:55:39 +00:00
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
|
|
def init(CONFIG, logger):
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
return Mod(CONFIG, logger)
|
|
|
|
|
|
|
|
|
|
|
|
class Mod:
|
|
|
|
def __init__(self, CONFIG, logging):
|
|
|
|
self.URL = "https://www.e-license.jp/el25/pc/"
|
|
|
|
self.CONFIG = CONFIG
|
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
self.logging = logging
|
|
|
|
self.targets = []
|
|
|
|
self.calender = None
|
2023-02-11 14:55:39 +00:00
|
|
|
|
|
|
|
self.ss = self.authenticate()
|
|
|
|
|
2023-02-12 01:58:49 +00:00
|
|
|
def __del__(self):
|
|
|
|
res = self.ss.get(
|
|
|
|
url = self.URL+"logout.action",
|
|
|
|
params = {
|
|
|
|
"b.schoolCd": self.CONFIG["school"],
|
|
|
|
"senisakiCd": 4,
|
|
|
|
})
|
|
|
|
res.encoding = "shift_jis"
|
|
|
|
self.calender = Calender(res.text)
|
|
|
|
|
|
|
|
self.logging.info("logout succeeded")
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
def cycle(self):
|
2023-02-11 16:57:09 +00:00
|
|
|
if len(self.targets) == 0:
|
|
|
|
return
|
|
|
|
|
2023-02-11 14:55:39 +00:00
|
|
|
res = self.ss.post(
|
|
|
|
url = self.URL+"p04a.action",
|
|
|
|
data = {
|
|
|
|
"b.processCd": "A",
|
2023-02-12 01:58:49 +00:00
|
|
|
"b.kamokuCd" : self.calender.params["kamokuCd"],
|
2023-02-11 14:55:39 +00:00
|
|
|
"b.page" : 1,
|
|
|
|
"b.schoolCd" : self.CONFIG["school"],
|
|
|
|
})
|
|
|
|
res.encoding = "shift_jis"
|
2023-02-11 16:57:09 +00:00
|
|
|
self.calender = Calender(res.text)
|
|
|
|
|
|
|
|
now = datetime.datetime.utcnow() + datetime.timedelta(hours=9)
|
|
|
|
today = now.strftime("%Y%m%d")
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
for target in self.targets:
|
|
|
|
for slot in target[1]:
|
|
|
|
begins = now.combine(now.date(), TIMETABLE[slot])
|
|
|
|
border = begins - datetime.timedelta(hours=2)
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
if target[0] != today or now < border:
|
|
|
|
if self.calender.checkAvailable(target[0], slot):
|
|
|
|
self.make_reserve(target[0], slot)
|
|
|
|
target[2]()
|
|
|
|
break
|
|
|
|
self.targets = []
|
2023-02-11 14:55:39 +00:00
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
def watch(self, date, slots, cb):
|
|
|
|
self.logging.debug("watch request accepted, {} {}".format(date, slots))
|
|
|
|
self.targets.append((date, slots, cb))
|
|
|
|
|
|
|
|
def make_reserve(self, date, slot):
|
|
|
|
res = self.ss.post(
|
|
|
|
url = self.URL+"p03a.action",
|
|
|
|
data = {
|
|
|
|
"b.schoolCd" : self.CONFIG["school"],
|
|
|
|
"b.processCd" : "V",
|
2023-02-12 01:58:49 +00:00
|
|
|
"b.kamokuCd" : self.calender.params["kamokuCd"],
|
|
|
|
"b.instructorTypeCd" : self.calender.params["instructorTypeCd"],
|
2023-02-11 16:57:09 +00:00
|
|
|
"b.dateInformationType": date,
|
|
|
|
"b.infoPeriodNumber" : slot,
|
2023-02-12 01:58:49 +00:00
|
|
|
"b.carModelCd" : self.calender.params["carModelCd"],
|
|
|
|
"b.instructorCd" : self.calender.params["instructorCd"],
|
2023-02-11 16:57:09 +00:00
|
|
|
"b.page" : 1,
|
|
|
|
"b.groupCd" : self.calender.group,
|
|
|
|
})
|
|
|
|
res.encoding = "shift_jis"
|
|
|
|
self.calender = Calender(res.text)
|
|
|
|
|
|
|
|
self.logging.info("reservation made, {}:{}".format(date, slots))
|
2023-02-11 14:55:39 +00:00
|
|
|
|
|
|
|
def authenticate(self):
|
|
|
|
ss = requests.Session()
|
|
|
|
try:
|
|
|
|
res = ss.post(
|
|
|
|
url = self.URL+"p01a.action",
|
|
|
|
data = {
|
|
|
|
"b.studentId" : self.CONFIG["username"],
|
|
|
|
"b.password" : self.CONFIG["password"],
|
|
|
|
"b.schoolCd" : self.CONFIG["school"],
|
|
|
|
"b.processCd" : "",
|
|
|
|
"b.kamokuCd" : "",
|
|
|
|
"method:doLogin" : "ログイン".encode("shift_jis"),
|
|
|
|
})
|
2023-02-11 16:57:09 +00:00
|
|
|
res.encoding = "shift_jis"
|
|
|
|
self.calender = Calender(res.text)
|
2023-02-11 14:55:39 +00:00
|
|
|
self.logging.info("authenticated")
|
|
|
|
return ss
|
|
|
|
except Exception as e:
|
|
|
|
self.logging.error("authentication failure ({})".format(e))
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
class Calender:
|
|
|
|
def __init__(self, text):
|
|
|
|
m = re.search(RE_ERROR, text)
|
|
|
|
if m is None:
|
|
|
|
m = re.search(RE_ALERT, text)
|
|
|
|
if m is not None:
|
|
|
|
raise Exception(m[1])
|
|
|
|
|
|
|
|
self.avails = re.findall(RE_AVAIL_SLOT, text)
|
|
|
|
|
2023-02-12 01:58:49 +00:00
|
|
|
self.params = {}
|
|
|
|
for name in RE_PARAMS:
|
|
|
|
m = re.search(RE_PARAMS[name], text)
|
|
|
|
if m is not None:
|
|
|
|
self.params[name] = m[1]
|
|
|
|
|
2023-02-11 16:57:09 +00:00
|
|
|
def checkAvailable(self, date, slot):
|
|
|
|
for avail in self.avails:
|
|
|
|
if avail[0] == date and avail[1] == str(slot):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
TIMETABLE = {
|
|
|
|
1: datetime.time( 9,20),
|
|
|
|
2: datetime.time(10,20),
|
|
|
|
3: datetime.time(11,20),
|
|
|
|
4: datetime.time(12,20),
|
|
|
|
5: datetime.time(14,10),
|
|
|
|
6: datetime.time(15,10),
|
|
|
|
7: datetime.time(16,10),
|
|
|
|
8: datetime.time(17,10),
|
|
|
|
9: datetime.time(18,10),
|
|
|
|
10: datetime.time(19,10),
|
|
|
|
}
|
|
|
|
|
|
|
|
RE_ALERT = re.compile(r"window\.alert\('(.*)'\)")
|
|
|
|
RE_ERROR = re.compile(r"<.*? class=\"errorTitle\">メッセージ<\/.*?>\s*<.*? class=\"errorDisp\">(.*?)<\/.*?>", re.MULTILINE)
|
|
|
|
RE_AVAIL_SLOT = re.compile(r"^\s*<a href=\"#\" onclick=\"sendContent\('(\d+)','(\d+)','V',document\.getElementById\('formId'\)\)\">$")
|
2023-02-12 01:58:49 +00:00
|
|
|
|
|
|
|
RE_PARAMS = {
|
|
|
|
"kamokuCd" : None,
|
|
|
|
"instructorTypeCd": None,
|
|
|
|
"carModelCd" : None,
|
|
|
|
"instructorCd" : None,
|
|
|
|
"groupCd" : None,
|
|
|
|
}
|
|
|
|
for name in RE_PARAMS:
|
|
|
|
RE_PARAMS[name] = re.compile(
|
|
|
|
r"<input type=\"hidden\" name=\"b.{0}\" value=\"(.*?)\" id=\"".format(name))
|