1
0
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:
positnuec
2026-07-18 10:20:43 +08:00
parent 189645b97a
commit fbe037322a
21 changed files with 715 additions and 637 deletions

View File

@@ -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)