mirror of
https://github.com/sui-feng-cb/AzurLaneAutoScript1.git
synced 2026-07-22 14:01:52 +08:00
Refactor: replace independent scheduled DowntimeFetch task with on-demand auto-fetch
This commit is contained in:
@@ -1,47 +1,83 @@
|
||||
import random
|
||||
from datetime import timedelta
|
||||
from __future__ import annotations
|
||||
|
||||
from module.config.utils import get_server_next_update
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from module.config.config import AzurLaneConfig
|
||||
from module.logger import logger
|
||||
from module.smart_mgmt.downtime_fetcher import DowntimeFetcher, FetchError
|
||||
from module.smart_mgmt.fetcher_test import DowntimeFetcherTest
|
||||
|
||||
|
||||
class DowntimeFetch:
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
DOWNTIME_FORMAT_RE = re.compile(
|
||||
r'(\d{4})-(\d{1,2})-(\d{1,2}) '
|
||||
r'(\d{1,2}):(\d{2})~(\d{1,2}):(\d{2})',
|
||||
)
|
||||
|
||||
def _set_downtime(self, window, title):
|
||||
self.config.modified['DowntimeMgmt.Downtime.Window'] = window
|
||||
self.config.modified['DowntimeMgmt.Downtime.Title'] = title
|
||||
self.config.update()
|
||||
|
||||
def _update_downtime(self) -> bool:
|
||||
"""Fetch downtime via fetcher and write cross-task config."""
|
||||
try:
|
||||
payload = DowntimeFetcher().run()
|
||||
except FetchError as e:
|
||||
# Expected operational failure (API error, collection missing, no cache);
|
||||
# degrade gracefully and let the caller reschedule
|
||||
logger.warning(f'Fetch downtime failed: {e}')
|
||||
return False
|
||||
except Exception:
|
||||
# Unexpected programming error; full traceback for diagnosis
|
||||
logger.exception('Unexpected error during downtime fetch')
|
||||
return False
|
||||
self._set_downtime(payload.get('window', ''), payload.get('title', ''))
|
||||
return True
|
||||
def parse_downtime(window: str) -> Optional[datetime]:
|
||||
"""
|
||||
Args:
|
||||
window (str): Window string in format 'YYYY-MM-DD HH:MM~HH:MM'.
|
||||
|
||||
def run(self):
|
||||
success = False
|
||||
if self.config.SERVER != 'cn':
|
||||
# Downtime fetch targets CN Bilibili notices only; skip on other servers
|
||||
logger.warning('Downtime fetch is only available for CN server, skip')
|
||||
Returns:
|
||||
Optional[datetime]:
|
||||
End datetime parsed from the window, or None if no pattern matched.
|
||||
"""
|
||||
match = DOWNTIME_FORMAT_RE.search(window)
|
||||
if not match:
|
||||
logger.warning(f'Failed to parse downtime window: {window}')
|
||||
return None
|
||||
year, month, day = int(match.group(1)), int(match.group(2)), int(match.group(3))
|
||||
end_hour, end_minute = int(match.group(6)), int(match.group(7))
|
||||
end_dt = datetime(year, month, day, end_hour, end_minute)
|
||||
logger.info(f'Parsed downtime window: end={end_dt:%Y-%m-%d %H:%M}')
|
||||
return end_dt
|
||||
|
||||
|
||||
def update_downtime(config: AzurLaneConfig, force_refresh=False) -> bool:
|
||||
"""Fetch downtime and write config."""
|
||||
try:
|
||||
payload = DowntimeFetcher().run(force_refresh=force_refresh)
|
||||
except FetchError as e:
|
||||
# Expected operational failure degrade
|
||||
logger.warning(f'Failed to fetch downtime: {e}')
|
||||
return False
|
||||
except Exception as e:
|
||||
# Unexpected programming error; full traceback for diagnosis
|
||||
logger.exception(f'Unexpected error: {e}')
|
||||
return False
|
||||
with config.multi_set():
|
||||
config.cross_set('DowntimeMgmt.Downtime.Window', payload.get('window', ''))
|
||||
config.cross_set('DowntimeMgmt.Downtime.Title', payload.get('title', ''))
|
||||
return True
|
||||
|
||||
|
||||
def get_downtime_end(config: AzurLaneConfig) -> Optional[datetime]:
|
||||
task = config.task.command
|
||||
if not config.cross_get(f'DowntimeMgmt.DowntimeStrategy.Manage{task}', False):
|
||||
return None
|
||||
if config.cross_get('DowntimeMgmt.DowntimeStrategy.AutoFetch', False):
|
||||
if config.SERVER == 'cn':
|
||||
update_downtime(config)
|
||||
else:
|
||||
success = self._update_downtime()
|
||||
if success:
|
||||
# Stagger execution across instances within 5 minutes after server update
|
||||
next_update = get_server_next_update(self.config.Scheduler_ServerUpdate)
|
||||
target = next_update + timedelta(seconds=random.randint(0, 300))
|
||||
self.config.task_delay(target=target)
|
||||
else:
|
||||
self.config.task_delay(success=False)
|
||||
logger.warning('Auto-fetch skipped as fetcher is for CN only, use window from config')
|
||||
else:
|
||||
logger.info('Auto-fetch disabled, use window from config')
|
||||
window = config.cross_get('DowntimeMgmt.Downtime.Window', None)
|
||||
if not window:
|
||||
return None
|
||||
return parse_downtime(window)
|
||||
|
||||
|
||||
def run_downtime_fetch(config: AzurLaneConfig):
|
||||
# Tool task entry is invoked with config bound to Alas by default,
|
||||
# rebind to DowntimeFetch.
|
||||
config.init_task('DowntimeFetch')
|
||||
if config.DowntimeFetch_FetcherTest:
|
||||
DowntimeFetcherTest().run()
|
||||
else:
|
||||
if config.SERVER != 'cn':
|
||||
logger.warning('Downtime fetch targets CN Bilibili notices, data may be irrelevant on other servers')
|
||||
update_downtime(config, force_refresh=True)
|
||||
|
||||
Reference in New Issue
Block a user