AzurLaneAutoScript/alas.py

398 lines
15 KiB
Python
Raw Normal View History

import os
import re
2022-01-09 19:01:31 +08:00
import threading
import time
from datetime import datetime
2021-09-14 17:32:23 +08:00
import inflection
from cached_property import cached_property
import module.config.server as server
2021-09-14 17:32:23 +08:00
from module.config.config import AzurLaneConfig, TaskEnd
2021-09-25 23:29:10 +08:00
from module.config.config_updater import ConfigUpdater
from module.config.utils import deep_get, deep_set
from module.exception import *
from module.logger import logger
class AzurLaneAutoScript:
2022-01-09 19:01:31 +08:00
2022-01-12 22:27:56 +08:00
stop_event: threading.Event = None
2022-01-09 19:01:31 +08:00
2021-09-14 17:32:23 +08:00
def __init__(self, config_name='alas'):
2022-01-24 00:07:55 +08:00
logger.hr('Start', level=0)
2021-09-14 17:32:23 +08:00
self.config_name = config_name
2021-09-25 23:29:10 +08:00
ConfigUpdater().update_config(config_name)
2021-09-14 17:32:23 +08:00
@cached_property
def config(self):
try:
config = AzurLaneConfig(config_name=self.config_name)
# Set server before loading any buttons.
server.server = deep_get(config.data, keys='Alas.Emulator.Server', default='cn')
return config
except RequestHumanTakeover:
logger.critical('Request human takeover')
exit(1)
except Exception as e:
logger.exception(e)
exit(1)
2021-09-14 17:32:23 +08:00
@cached_property
def device(self):
try:
from module.device.device import Device
device = Device(config=self.config)
return device
2022-01-24 00:07:55 +08:00
except RequestHumanTakeover:
logger.critical('Request human takeover')
exit(1)
except Exception as e:
logger.exception(e)
exit(1)
2020-03-29 01:22:46 +08:00
def run(self, command):
try:
self.__getattribute__(command)()
return True
except TaskEnd:
return True
except GameNotRunningError as e:
logger.warning(e)
self.config.task_call('Restart')
return True
except (GameStuckError, GameTooManyClickError) as e:
logger.warning(e)
self.save_error_log()
logger.warning(f'Game stuck, {self.config.Emulator_PackageName} will be restarted in 10 seconds')
logger.warning('If you are playing by hand, please stop Alas')
self.config.task_call('Restart')
self.device.sleep(10)
return False
except LogisticsRefreshBugHandler as e:
logger.warning(e)
self.save_error_log()
self.config.task_call('Restart')
self.device.sleep(10)
return False
except ScriptError as e:
logger.critical(e)
logger.critical('This is likely to be a mistake of developers, but sometimes just random issues')
exit(1)
except RequestHumanTakeover:
logger.critical('Request human takeover')
exit(1)
except Exception as e:
logger.exception(e)
self.save_error_log()
exit(1)
def save_error_log(self):
"""
Save last 60 screenshots in ./log/error/<timestamp>
Save logs to ./log/error/<timestamp>/log.txt
"""
from module.handler.sensitive_info import handle_sensitive_image, handle_sensitive_logs
if self.config.Error_SaveError:
2021-10-12 14:15:23 +08:00
if not os.path.exists('./log/error'):
os.mkdir('./log/error')
folder = f'./log/error/{int(time.time() * 1000)}'
logger.warning(f'Saving error: {folder}')
os.mkdir(folder)
for data in self.device.screenshot_deque:
image_time = datetime.strftime(data['time'], '%Y-%m-%d_%H-%M-%S-%f')
image = handle_sensitive_image(data['image'])
image.save(f'{folder}/{image_time}.png')
with open(logger.log_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
start = 0
for index, line in enumerate(lines):
2022-01-24 00:07:55 +08:00
line = line.strip(' \r\t\n')
if re.match('^═{15,}$', line):
start = index
lines = lines[start - 2:]
lines = handle_sensitive_logs(lines)
with open(f'{folder}/log.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
def restart(self):
from module.handler.login import LoginHandler
LoginHandler(self.config, device=self.device).app_restart()
def start(self):
from module.handler.login import LoginHandler
LoginHandler(self.config, device=self.device).app_start()
def goto_main(self):
from module.ui.ui import UI
UI(self.config, device=self.device).ui_goto_main()
2021-09-14 17:32:23 +08:00
def research(self):
from module.research.research import RewardResearch
2021-09-14 17:32:23 +08:00
RewardResearch(config=self.config, device=self.device).run()
2021-09-14 17:32:23 +08:00
def commission(self):
from module.commission.commission import RewardCommission
2021-09-14 17:32:23 +08:00
RewardCommission(config=self.config, device=self.device).run()
2021-09-14 17:32:23 +08:00
def tactical(self):
from module.tactical.tactical_class import RewardTacticalClass
2021-09-14 17:32:23 +08:00
RewardTacticalClass(config=self.config, device=self.device).run()
def dorm(self):
from module.dorm.dorm import RewardDorm
RewardDorm(config=self.config, device=self.device).run()
2021-09-23 22:04:03 +08:00
def meowfficer(self):
from module.meowfficer.meowfficer import RewardMeowfficer
2021-09-23 22:04:03 +08:00
RewardMeowfficer(config=self.config, device=self.device).run()
def guild(self):
from module.guild.guild_reward import RewardGuild
RewardGuild(config=self.config, device=self.device).run()
def reward(self):
from module.reward.reward import Reward
Reward(config=self.config, device=self.device).run()
def shop_frequent(self):
from module.shop.shop_reward import RewardShop
RewardShop(config=self.config, device=self.device).run_frequent()
def shop_once(self):
from module.shop.shop_reward import RewardShop
RewardShop(config=self.config, device=self.device).run_once()
2021-10-03 00:36:14 +08:00
def shipyard(self):
from module.shipyard.shipyard_reward import RewardShipyard
RewardShipyard(config=self.config, device=self.device).run()
def gacha(self):
from module.gacha.gacha_reward import RewardGacha
RewardGacha(config=self.config, device=self.device).run()
2021-10-16 06:29:28 +08:00
def data_key(self):
from module.data_key.data_key import RewardDataKey
2021-10-16 06:29:28 +08:00
RewardDataKey(config=self.config, device=self.device).run()
2021-11-27 12:55:09 +08:00
def supply_pack(self):
from module.supply_pack.supply_pack import SupplyPack
SupplyPack(config=self.config, device=self.device).run()
2021-12-03 17:32:10 +08:00
def battle_pass(self):
from module.battle_pass.battle_pass import BattlePass
BattlePass(config=self.config, device=self.device).run()
def daily(self):
from module.daily.daily import Daily
Daily(config=self.config, device=self.device).run()
def hard(self):
from module.hard.hard import CampaignHard
CampaignHard(config=self.config, device=self.device).run()
def exercise(self):
from module.exercise.exercise import Exercise
Exercise(config=self.config, device=self.device).run()
def sos(self):
from module.sos.sos import CampaignSos
CampaignSos(config=self.config, device=self.device).run()
2021-10-11 11:38:10 +08:00
def war_archives(self):
from module.war_archives.war_archives import CampaignWarArchives
CampaignWarArchives(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
def event_ab(self):
from module.event.campaign_ab import CampaignAB
CampaignAB(config=self.config, device=self.device).run()
def event_cd(self):
from module.event.campaign_cd import CampaignCD
CampaignCD(config=self.config, device=self.device).run()
def event_sp(self):
from module.event.campaign_sp import CampaignSP
CampaignSP(config=self.config, device=self.device).run()
2022-01-15 22:08:17 +08:00
def maritime_escort(self):
from module.event.maritime_escort import MaritimeEscort
MaritimeEscort(config=self.config, device=self.device).run()
def opsi_ash_assist(self):
from module.os_ash.ash import AshBeaconAssist
2021-09-25 00:38:53 +08:00
AshBeaconAssist(config=self.config, device=self.device).run()
def opsi_explore(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_explore()
def opsi_shop(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_shop()
def opsi_daily(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_daily()
def opsi_obscure(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_obscure()
def opsi_abyssal(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_abyssal()
def opsi_stronghold(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_stronghold()
def opsi_meowfficer_farming(self):
from module.campaign.os_run import OSCampaignRun
OSCampaignRun(config=self.config, device=self.device).opsi_meowfficer_farming()
def main(self):
from module.campaign.run import CampaignRun
CampaignRun(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
def event(self):
from module.campaign.run import CampaignRun
CampaignRun(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
def raid(self):
from module.raid.run import RaidRun
RaidRun(config=self.config, device=self.device).run()
2021-09-25 12:14:33 +08:00
def c72_mystery_farming(self):
from module.campaign.run import CampaignRun
CampaignRun(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
def c122_medium_leveling(self):
from module.campaign.run import CampaignRun
CampaignRun(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
def c124_large_leveling(self):
from module.campaign.run import CampaignRun
2021-09-25 12:14:33 +08:00
CampaignRun(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
2021-09-25 12:14:33 +08:00
def gems_farming(self):
from module.campaign.gems_farming import GemsFarming
GemsFarming(config=self.config, device=self.device).run(
name=self.config.Campaign_Name, folder=self.config.Campaign_Event, mode=self.config.Campaign_Mode)
2022-01-09 19:01:31 +08:00
def wait_until(self, future):
"""
Wait until a specific time.
Args:
future (datetime):
"""
seconds = future.timestamp() - datetime.now().timestamp() + 1
2022-01-09 19:01:31 +08:00
if seconds <= 0:
logger.warning(f'Wait until {str(future)}, but sleep length < 0, skip waiting')
2022-01-16 15:58:36 +08:00
return
2022-01-09 19:01:31 +08:00
2022-01-12 22:27:56 +08:00
if self.stop_event is not None:
2022-01-09 19:01:31 +08:00
self.stop_event.wait(seconds)
if self.stop_event.is_set():
logger.info("Update event detected")
2022-01-16 15:58:36 +08:00
logger.info(f"[{self.config_name}] exited. Reason: Update")
2022-01-09 19:01:31 +08:00
exit(0)
else:
time.sleep(seconds)
def get_next_task(self):
"""
Returns:
str: Name of the next task.
"""
task = self.config.get_next()
self.config.task = task
self.config.bind(task)
if task.next_run > datetime.now():
logger.info(f'Wait until {task.next_run} for task `{task.command}`')
method = self.config.Optimization_WhenTaskQueueEmpty
if method == 'close_game':
logger.info('Close game during wait')
self.device.app_stop()
self.wait_until(task.next_run)
self.run('start')
elif method == 'goto_main':
logger.info('Goto main page during wait')
self.run('goto_main')
self.wait_until(task.next_run)
elif method == 'stay_there':
self.wait_until(task.next_run)
else:
logger.warning(f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there')
self.wait_until(task.next_run)
AzurLaneConfig.is_hoarding_task = False
return task.command
2021-09-14 17:32:23 +08:00
def loop(self):
logger.set_file_logger(self.config_name)
logger.info(f'Start scheduler loop: {self.config_name}')
2021-09-25 00:51:51 +08:00
is_first = True
failure_record = {}
2021-09-14 17:32:23 +08:00
while 1:
2022-01-12 22:27:56 +08:00
if self.stop_event is not None:
2022-01-09 19:01:31 +08:00
if self.stop_event.is_set():
logger.info("Update event detected")
logger.info(f"Alas [{self.config_name}] exited.")
break
task = self.get_next_task()
# Skip first restart
if is_first and task == 'Restart':
2021-09-25 00:51:51 +08:00
logger.info('Skip task `Restart` at scheduler start')
self.config.task_delay(server_update=True)
del self.__dict__['config']
continue
2021-09-25 00:51:51 +08:00
# Run
logger.info(f'Scheduler: Start task `{task}`')
self.device.stuck_record_clear()
self.device.click_record_clear()
self.device.screenshot()
logger.hr(task, level=0)
success = self.run(inflection.underscore(task))
logger.info(f'Scheduler: End task `{task}`')
2021-09-25 00:51:51 +08:00
is_first = False
# Check failures
failed = deep_get(failure_record, keys=task, default=0)
failed = 0 if success else failed + 1
deep_set(failure_record, keys=task, value=failed)
if failed >= 3:
logger.critical(f"Task `{task}` failed 3 or more times.")
logger.critical("Possible reason #1: You haven't used it correctly. "
"Please read the help text of the options.")
logger.critical("Possible reason #2: There is a problem with this task. "
"Please contact developers or try to fix it yourself.")
logger.critical('Request human takeover')
exit(1)
if success:
del self.__dict__['config']
continue
elif self.config.Error_HandleError:
# self.config.task_delay(success=False)
del self.__dict__['config']
continue
else:
2021-09-14 17:32:23 +08:00
break
2021-04-17 22:20:21 +08:00
2021-09-14 17:32:23 +08:00
if __name__ == '__main__':
alas = AzurLaneAutoScript()
alas.loop()