def _alas_thread_update_config(self) -> None: modified = {} valid = [] invalid = [] while self.alive: try: d = self.modified_config_queue.get(timeout=10) config_name = self.alas_name except queue.Empty: continue modified[self.idx_to_path[d["name"]]] = parse_pin_value(d["value"]) while True: try: d = self.modified_config_queue.get(timeout=1) modified[self.idx_to_path[d["name"]]] = parse_pin_value( d["value"]) except queue.Empty: config = read_file(filepath_config(config_name)) for k, v in modified.copy().items(): validate = deep_get(self.ALAS_ARGS, k + ".validate") if not len(str(v)): default = deep_get(self.ALAS_ARGS, k + ".value") deep_set(config, k, default) valid.append(self.path_to_idx[k]) modified[k] = default elif not validate or re_fullmatch(validate, v): deep_set(config, k, v) valid.append(self.path_to_idx[k]) else: modified.pop(k) invalid.append(self.path_to_idx[k]) logger.warning( f"Invalid value {v} for key {k}, skip saving.") # toast(t("Gui.Toast.InvalidConfigValue").format( # t('.'.join(k.split('.')[1:] + ['name']))), # duration=0, position='right', color='warn') self.pin_remove_invalid_mark(valid) self.pin_set_invalid_mark(invalid) if modified: toast( t("Gui.Toast.ConfigSaved"), duration=1, position="right", color="success", ) logger.info( f"Save config {filepath_config(config_name)}, {dict_to_kv(modified)}" ) write_file(filepath_config(config_name), config) modified.clear() valid.clear() invalid.clear() break
def set_group(self, group, arg_dict, config, task): group_name = group[0] with use_scope(f'group_{group_name}'): put_text(t(f"{group_name}._info.name")) group_help = t(f"{group_name}._info.help") if group_help != "": put_text(group_help) put_html('<hr class="hr-group">') for arg, d in deep_iter(arg_dict, depth=1): arg = arg[0] arg_type = d['type'] if arg_type == 'disable': continue value = deep_get(config, f'{task}.{group_name}.{arg}', d['value']) value = str(value) if isinstance(value, datetime) else value # Option options = deep_get(d, 'option', None) if options: option = [] for opt in options: o = { "label": t(f"{group_name}.{arg}.{opt}"), "value": opt } if value == opt: o["selected"] = True option.append(o) else: option = None # Help arg_help = t(f"{group_name}.{arg}.help") if arg_help == "" or not arg_help: arg_help = None # Invalid feedback invalid_feedback = t("Gui.Text.InvalidFeedBack").format( d['value']) get_output( arg_type=arg_type, name=self.path_to_idx[f"{task}.{group_name}.{arg}"], title=t(f"{group_name}.{arg}.name"), arg_help=arg_help, value=value, options=option, invalid_feedback=invalid_feedback, ).show()
def _os_explore_task_delay(self): """ Delay other OpSi tasks during os_explore """ logger.info('Delay other OpSi tasks during OpsiExplore') next_run = self.config.Scheduler_NextRun for task in [ 'OpsiObscure', 'OpsiAbyssal', 'OpsiStronghold', 'OpsiMeowfficerFarming' ]: keys = f'{task}.Scheduler.NextRun' current = deep_get(self.config.data, keys=keys, default=datetime(2020, 1, 1, 0, 0)) if current < next_run: logger.info(f'Delay task `{task}` to {next_run}') self.config.modified[keys] = next_run # ResetActionPointPreserve # Unbound attribute, default to 500 preserve = self.config.OpsiMeowfficerFarming_ActionPointPreserve logger.info( f'Set OpsiMeowfficerFarming.ActionPointPreserve to {preserve}') self.config.modified[ 'OpsiMeowfficerFarming.OpsiMeowfficerFarming.ActionPointPreserve'] = preserve self.config.update()
def next_key(): if V.idx + 1 > total: V.idx = -1 V.idx += 1 if V.untranslated_only: while True: # print(V.idx) key = deep_get(dict_lang[V.lang], list_path[V.idx]) if list_path[V.idx] == key or list_path[V.idx].split( '.')[2] == key: break else: V.idx += 1 if V.idx + 1 > total: V.idx = 0 break (V.group, V.arg, V.key) = tuple(list_path[V.idx].split('.')) V.group_idx = list_group.index(V.group) V.arg_idx = list_arg.index(V.arg, V.group_idx) V.args = list(dict_lang["zh-CN"][V.group].keys()) V.key_idx = list_key.index(V.key, V.arg_idx) V.keys = list(dict_lang["zh-CN"][V.group][V.arg].keys())
def _save_config(self, modified: Dict[str, str], config_name: str) -> None: try: valid = [] invalid = [] config = State.config_updater.read_file(config_name) for k, v in modified.copy().items(): valuetype = deep_get(self.ALAS_ARGS, k + ".valuetype") v = parse_pin_value(v, valuetype) validate = deep_get(self.ALAS_ARGS, k + ".validate") if not len(str(v)): default = deep_get(self.ALAS_ARGS, k + ".value") deep_set(config, k, default) valid.append(self.path_to_idx[k]) modified[k] = default elif not validate or re_fullmatch(validate, v): deep_set(config, k, v) valid.append(self.path_to_idx[k]) # update Emotion Record if Emotion Value is changed if "Emotion" in k and "Value" in k: k = k.split(".") k[-1] = k[-1].replace("Value", "Record") k = ".".join(k) v = datetime.now().strftime("%Y-%m-%d %H:%M:%S") modified[k] = v deep_set(config, k, v) valid.append(self.path_to_idx[k]) pin[self.path_to_idx[k]] = v else: modified.pop(k) invalid.append(self.path_to_idx[k]) logger.warning(f"Invalid value {v} for key {k}, skip saving.") self.pin_remove_invalid_mark(valid) self.pin_set_invalid_mark(invalid) if modified: toast( t("Gui.Toast.ConfigSaved"), duration=1, position="right", color="success", ) logger.info( f"Save config {filepath_config(config_name)}, {dict_to_kv(modified)}" ) State.config_updater.write_file(config_name, config) except Exception as e: logger.exception(e)
def update_form(): input_update('arg', options=V.args, value=V.arg) input_update('key', options=V.keys, value=V.key) for L in LANGUAGES: input_update(L, value=deep_get(dict_lang[L], f'{V.group}.{V.arg}.{V.key}', 'Key not found!')) old = deep_get(dict_lang[V.lang], f'{V.group}.{V.arg}.{V.key}', 'Key not found!') input_update( V.lang, value=None if V.clear else old, help_text=f'{V.group}.{V.arg}.{V.key}', placeholder=old, )
def loop(self): logger.set_file_logger(self.config_name) logger.info(f'Start scheduler loop: {self.config_name}') is_first = True failure_record = {} while 1: if self.stop_event is not None: 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': logger.info('Skip task `Restart` at scheduler start') self.config.task_delay(server_update=True) del self.__dict__['config'] continue # 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}`') 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: break
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)
def run_process(config_name, func: str, q: queue.Queue, e: threading.Event = None) -> None: # Setup logger set_file_logger(name=config_name) set_func_logger(func=q.put) # Set server before loading any buttons. import module.config.server as server from module.config.config import AzurLaneConfig AzurLaneConfig.stop_event = e config = AzurLaneConfig(config_name=config_name) server.server = deep_get(config.data, keys="Alas.Emulator.Server", default="cn") try: # Run alas if func == "Alas": from alas import AzurLaneAutoScript if e is not None: AzurLaneAutoScript.stop_event = e AzurLaneAutoScript(config_name=config_name).loop() elif func == "Daemon": from module.daemon.daemon import AzurLaneDaemon AzurLaneDaemon(config=config_name, task="Daemon").run() elif func == "OpsiDaemon": from module.daemon.os_daemon import AzurLaneDaemon AzurLaneDaemon(config=config_name, task="OpsiDaemon").run() elif func == "AzurLaneUncensored": from module.daemon.uncensored import AzurLaneUncensored AzurLaneUncensored(config=config_name, task="AzurLaneUncensored").run() elif func == "Benchmark": from module.daemon.benchmark import Benchmark Benchmark(config=config_name, task="Benchmark").run() elif func == "GameManager": from module.daemon.game_manager import GameManager GameManager(config=config_name, task="GameManager").run() else: logger.critical("No function matched") logger.info(f"[{config_name}] exited. Reason: Finish\n") except Exception as e: logger.exception(e)
def is_in_opsi_explore(self): """ Returns: bool: If task OpsiExplore is under scheduling. """ enable = deep_get(self.config.data, keys='OpsiExplore.Scheduler.Enable', default=False) next_run = deep_get(self.config.data, keys='OpsiExplore.Scheduler.NextRun', default=DEFAULT_TIME) next_reset = get_os_next_reset() logger.attr('OpsiNextReset', next_reset) logger.attr('OpsiExplore', (enable, next_run)) if enable and next_run < next_reset: logger.info( 'OpsiExplore is still running, accept missions only. ' 'Missions will be finished when OpsiExplore visits every zones, ' 'no need to worry they are left behind.') return True else: logger.info('Not in OpsiExplore, able to do OpsiDaily') return False
def run_process(config_name, func: str, q: queue.Queue, e: threading.Event = None) -> None: # Setup logger qh = QueueHandler(q) formatter = logging.Formatter( fmt='%(asctime)s.%(msecs)03d | %(levelname)s | %(message)s', datefmt='%H:%M:%S') webconsole = logging.StreamHandler(stream=qh) webconsole.setFormatter(formatter) logger.addHandler(webconsole) # Set server before loading any buttons. import module.config.server as server from module.config.config import AzurLaneConfig AzurLaneConfig.stop_event = e config = AzurLaneConfig(config_name=config_name) server.server = deep_get(config.data, keys='Alas.Emulator.Server', default='cn') try: # Run alas if func == 'Alas': from alas import AzurLaneAutoScript if e is not None: AzurLaneAutoScript.stop_event = e AzurLaneAutoScript(config_name=config_name).loop() elif func == 'Daemon': from module.daemon.daemon import AzurLaneDaemon AzurLaneDaemon(config=config_name, task='Daemon').run() elif func == 'OpsiDaemon': from module.daemon.os_daemon import AzurLaneDaemon AzurLaneDaemon(config=config_name, task='OpsiDaemon').run() elif func == 'AzurLaneUncensored': from module.daemon.uncensored import AzurLaneUncensored AzurLaneUncensored(config=config_name, task='AzurLaneUncensored').run() elif func == 'Benchmark': from module.daemon.benchmark import Benchmark Benchmark(config=config_name, task='Benchmark').run() elif func == 'GameManager': from module.daemon.game_manager import GameManager GameManager(config=config_name, task='GameManager').run() else: logger.critical("No function matched") logger.info(f"[{config_name}] exited. Reason: Finish") except Exception as e: logger.exception(e)
def handle_commission_notice(self): """ Check commission notice. If found, stop current task and call commission. Raises: TaskEnd: If found commission notice. Pages: in: page_campaign """ if deep_get(self.config.data, keys='Commission.Scheduler.Enable', default=False): if self.campaign.commission_notice_show_at_campaign(): logger.info('Commission notice found') self.config.task_call('Commission') self.config.task_stop('Commission notice found')
def _disable_tasks(self, tasks): """ Args: tasks (list[str]): Task name """ for task in tasks: keys = f'{task}.Scheduler.Enable' logger.info(f'Disable task `{task}`') self.config.modified[keys] = False for task in ['GemsFarming']: if deep_get(self.config.data, keys=f'{task}.Campaign.Event', default='campaign_main') != 'campaign_main': logger.info(f'Reset GemsFarming to 2-4') self.config.modified[f'{task}.Campaign.Name'] = '2-4' self.config.modified[ f'{task}.Campaign.Event'] = 'campaign_main' logger.info(f'Reset event time limit') self.config.modified['EventGeneral.EventGeneral.TimeLimit'] = datetime( 2020, 1, 1, 0, 0) self.config.update()
def _is_in_os_explore(self): return deep_get(self.config.data, keys='OpsiExplore.Scheduler.Enable', default=False)
def get_inputs(): out = [] old = deep_get(dict_lang[V.lang], f'{V.group}.{V.arg}.{V.key}', 'Key not found!') out.append( input( name=V.lang, label=V.lang, value=None if V.clear else old, help_text=f'{V.group}.{V.arg}.{V.key}', placeholder=old, )) out.append( select(name='group', label='Group', options=V.groups, value=V.group, onchange=lambda g: update_var(group=g), required=True)) out.append( select(name='arg', label='Arg', options=V.args, value=V.arg, onchange=lambda a: update_var(arg=a), required=True)) out.append( select(name='key', label='Key', options=V.keys, value=V.key, onchange=lambda k: update_var(key=k), required=True)) _LANGUAGES = LANGUAGES.copy() _LANGUAGES.remove(V.lang) for L in _LANGUAGES: out.append( input(name=L, label=L, readonly=True, value=deep_get(dict_lang[L], f'{V.group}.{V.arg}.{V.key}', 'Key not found!'))) out.append( actions(name='action', buttons=[ { "label": "Next", "value": 'Next', "type": "submit", "color": "success" }, { "label": "Next without save", "value": 'Skip', "type": "submit", "color": "secondary" }, { "label": "Submit", "value": "Submit", "type": "submit", "color": "primary" }, { "label": "Quit and save", "type": "cancel", "color": "secondary" }, ])) return out