diff --git a/LibChecker.py b/LibChecker.py index c03be86..a7a6df2 100644 --- a/LibChecker.py +++ b/LibChecker.py @@ -44,9 +44,9 @@ class LibChecker(LibOperator): seconds: float ) -> str: - hours = int(seconds // 3600) - minutes = int(seconds % 3600 // 60) - seconds = int(seconds % 60) + hours = int(seconds//3600) + minutes = int(seconds%3600//60) + seconds = int(seconds%60) return f"{hours} 时 {minutes} 分 {seconds} 秒" @@ -67,6 +67,44 @@ class LibChecker(LibOperator): return True + def __decodeReserveTime( + self, + time_element + ) -> dict: + + time_str = time_element.text.strip() + today = datetime.now().date() + if "明天" in time_str: + target_date = today + timedelta(days=1) + date = target_date.strftime("%Y-%m-%d") + elif "今天" in time_str: + target_date = today + date = target_date.strftime("%Y-%m-%d") + elif "昨天" in time_str: + target_date = today - timedelta(days=1) + date = target_date.strftime("%Y-%m-%d") + else: + date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", time_str) + if date_match: + date = date_match.group(1) + else: + date = "" + time_match = re.search(r"(\d{1,2}:\d{2}) -- (\d{1,2}:\d{2})", time_str) + if time_match: + begin_time = time_match.group(1) + end_time = time_match.group(2) + else: + begin_time = "" + end_time = "" + return { + "date": date, + "time": { + "begin": begin_time, + "end": end_time + } + } + + def __decodeReserveInfo( self, info_elements @@ -108,42 +146,80 @@ class LibChecker(LibOperator): By.CSS_SELECTOR, "a" ) except: - return None - # process time element to get the date string - time_str = time_element.text.strip() - today = datetime.now().date() - if "明天" in time_str: - target_date = today + timedelta(days=1) - date_str = target_date.strftime("%Y-%m-%d") - elif "今天" in time_str: - target_date = today - date_str = target_date.strftime("%Y-%m-%d") - elif "昨天" in time_str: - target_date = today - timedelta(days=1) - date_str = target_date.strftime("%Y-%m-%d") - else: - date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", time_str) - if date_match: - date_str = date_match.group(1) - else: - date_str = "" - time_match = re.search(r"(\d{1,2}:\d{2}) -- (\d{1,2}:\d{2})", time_str) - if time_match: - begin_time = time_match.group(1) - end_time = time_match.group(2) - else: - time_str = "" + return { + "date": "", + "time": {"begin": "", "end": ""}, + "info": {"location": "", "status": ""} + } + time = self.__decodeReserveTime(time_element) info = self.__decodeReserveInfo(info_elements) return { - "date": date_str, - "time": { - "begin": begin_time, - "end": end_time, - }, + "date": time["date"], + "time": time["time"], "info": info } + def __decodeReserveRecords( + self, + reservations + ) -> list: + + records = [] + + for reservation in reservations: + record = self.__decodeReserveRecord(reservation) + if record["date"] == "": + record = None + if record["time"] == {"begin": "", "end": ""}: + record = None + records.append(record) + return records + + + def __loadReserveRecords( + self + ) -> list: + try: + # check if there's any reservation on the date + WebDriverWait(self.__driver, 2).until( + EC.presence_of_element_located((By.CSS_SELECTOR, ".myReserveList > dl")) + ) + reservations = self.__driver.find_elements( + By.CSS_SELECTOR, ".myReserveList > dl:not(#moreBlock)" + ) + return reservations + except: + self._showTrace("加载预约记录失败 !") + return None + + + def __showMoreReserveRecords( + self + ) -> bool: + + # load new reservations if still not sure + try: + WebDriverWait(self.__driver, 0.1).until( + EC.element_to_be_clickable((By.ID, "moreBtn")) + ) + except: + # the reservation is the last one + return False + try: + more_btn = self.__driver.find_element(By.ID, "moreBtn") + if more_btn.is_displayed() and more_btn.is_enabled(): + self.__driver.execute_script("arguments[0].scrollIntoView(true);", more_btn) + self.__driver.execute_script("arguments[0].click();", more_btn) + return True + else: + self._showTrace("用户无法加载更多预约记录") + return False + except: + self._showTrace("加载更多预约记录失败 !") + return False + + def __getReserveRecord( self, wanted_date: str, @@ -154,7 +230,6 @@ class LibChecker(LibOperator): self._showTrace("日期未指定, 无法检查当前预约状态") return None self._showTrace(f"正在检查用户在 {wanted_date} 是否有预约状态为 {wanted_status} 的预约记录......") - date_obj = datetime.strptime(wanted_date, "%Y-%m-%d").date() checked_count = 0 max_check_times = 6 # we only check (4*(6-1)=)20 reservations, the last time cant be checked @@ -162,59 +237,30 @@ class LibChecker(LibOperator): if not self.__navigateToReserveRecordPage(): return None for _ in range(max_check_times): - try: - # check if there's any reservation on the date - WebDriverWait(self.__driver, 2).until( - EC.presence_of_element_located((By.CSS_SELECTOR, ".myReserveList > dl")) - ) - reservations = self.__driver.find_elements( - By.CSS_SELECTOR, ".myReserveList > dl:not(#moreBlock)" - ) - except: - self._showTrace("加载预约记录失败 !") + reservations = self.__loadReserveRecords() + if reservations is None: return None - for i in range(checked_count, len(reservations)): # the last one is load button - reservation = reservations[i] - record = self.__decodeReserveRecord(reservation) + records = self.__decodeReserveRecords(reservations[checked_count:]) + for record in records: + checked_count += 1 if record is None: continue - record_date = record["date"] - record_time = record["time"] - status = record["info"]["status"] - location = record["info"]["location"] - if record_date == "" or record_time == {"begin": "", "end": ""}: + # record date is later than the given date, check the next one + if datetime.strptime(record["date"], "%Y-%m-%d").date() >\ + datetime.strptime(wanted_date, "%Y-%m-%d").date(): continue - is_wanted = (status == wanted_status) - # reservation is later than the given date, check the next one - if datetime.strptime(record_date, "%Y-%m-%d").date() > date_obj: - continue - # reservation is earlier than the given date, can reserve - if datetime.strptime(record_date, "%Y-%m-%d").date() < date_obj: + # record date is earlier than the given date, so there is no wanted record + if datetime.strptime(record["date"], "%Y-%m-%d").date() <\ + datetime.strptime(wanted_date, "%Y-%m-%d").date(): return None - # query the wanted status - if is_wanted: + if record["info"]["status"] == wanted_status: self._showTrace( - f"寻找到用户第 {i + 1} 条状态为 {wanted_status} 的预约记录, " - f"详细信息: {record_date} {record_time['begin']} - {record_time['end']} {location}" + f"寻找到用户第 {checked_count} 条状态为 {wanted_status} 的预约记录, " + f"详细信息: {record["date"]} " + f"{record["time"]["begin"]} - {record["time"]["end"]} {record["info"]["location"]}" ) - return { - "index": i, - "date": record_date, - "time": record_time, - "status": wanted_status - } - checked_count = len(reservations) - # load new reservations if still not sure - try: - more_btn = self.__driver.find_element(By.ID, "moreBtn") - if more_btn.is_displayed() and more_btn.is_enabled(): - self.__driver.execute_script("arguments[0].scrollIntoView(true);", more_btn) - self.__driver.execute_script("arguments[0].click();", more_btn) - else: - self._showTrace("用户无法加载更多预约记录") - break - except: - self._showTrace("加载更多预约记录失败 !") + return record + if not self.__showMoreReserveRecords(): break return None @@ -252,21 +298,21 @@ class LibChecker(LibOperator): if time_diff_seconds < -30*60: self._showTrace( f"用户在 {date} 的预约开始时间为 {begin_time}, " - f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法签到" + f"当前距离预约开始时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法签到" ) return False # before in 30 minutes, can checkin elif -30*60 <= time_diff_seconds < 0: self._showTrace( f"用户在 {date} 的预约开始时间为 {begin_time}, " - f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" + f"当前距离预约开始时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" ) return True # past less than 30 minutes, can checkin - elif 0 <= time_diff_seconds < 30*60: + elif 0 <= time_diff_seconds < 30*60 - 5: # spare 5 seconds for the checkin process self._showTrace( f"用户在 {date} 的预约开始时间为 {begin_time}, " - f"当前时间已经 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" + f"当前距离预约开始时间已经过去 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以签到" ) return True self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到") @@ -286,17 +332,15 @@ class LibChecker(LibOperator): time_diff = end_time - datetime.now() time_diff_seconds = time_diff.total_seconds() # a using record is definitely after the begin time + trace_msg = ( + f"用户在 {date} 的预约结束时间为 {end_time}, " + f"当前距离预约结束时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}" + ) if abs(time_diff_seconds) < 120*60: - self._showTrace( - f"用户在 {date} 的预约结束时间为 {end_time}, " - f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 可以续约" - ) + self._showTrace(f"{trace_msg}, 可以续约") return True else: - self._showTrace( - f"用户在 {date} 的预约结束时间为 {end_time}, " - f"距离当前时间还有 {self.__formatDiffTime(abs(time_diff_seconds))}, 无法续约" - ) + self._showTrace(f"{trace_msg}, 无法续约") return False self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约") return False