diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a680084..154267db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -137,7 +137,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -176,7 +176,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -244,7 +244,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -322,7 +322,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -361,7 +361,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_debug.spec - name: Run artifact run: | @@ -390,7 +390,7 @@ jobs: - name: Run packing run: | cp ./OlivOS/hook.py ./OlivOS/hook_bak.py - cp ./OlivOS/hook_pack.py ./OlivOS/hook.py + cp ./OlivOS/hook_pack_debug.py ./OlivOS/hook.py pyinstaller ./main_mac.spec - name: Run artifact run: | diff --git a/OlivOS/API.py b/OlivOS/API.py index 7b7114be..7ffe2329 100644 --- a/OlivOS/API.py +++ b/OlivOS/API.py @@ -21,6 +21,8 @@ import hashlib import time import traceback +import inspect +import ctypes from functools import wraps @@ -1423,18 +1425,42 @@ class StoppableThread(threading.Thread): def __init__(self, *args, **kwargs): super(StoppableThread, self).__init__(*args, **kwargs) self._stop_event = threading.Event() + self.root = None def terminate(self): + if self.root is not None: + try: + self.root.on_terminate() + except Exception as e: + traceback.print_exc() self._stop_event.set() + self.stop_thread() def stop(self): - self._stop_event.set() + self.terminate() def join(self): pass def stopped(self): return self._stop_event.is_set() + + def _async_raise(self, tid, exctype): + """raises the exception, performs cleanup if needed""" + tid = ctypes.c_long(tid) + if not inspect.isclass(exctype): + exctype = type(exctype) + res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) + if res == 0: + raise ValueError("invalid thread id") + elif res != 1: + # """if it returns a number greater than one, you're in trouble, + # and you should call it again with exc=NULL to revert the effect""" + ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) + raise SystemError("PyThreadState_SetAsyncExc failed") + + def stop_thread(self): + self._async_raise(self.ident, SystemExit) class Proc_templet(object): @@ -1460,6 +1486,7 @@ def __init__(self, rx_queue, tx_queue, control_queue, logger_proc, scan_interval self.rx_queue = rx_queue self.tx_queue = tx_queue self.control_queue = control_queue + self.control_rx_queue = multiprocessing.Queue() self.logger_proc = logger_proc self.scan_interval = scan_interval self.dead_interval = dead_interval @@ -1467,15 +1494,44 @@ def __init__(self, rx_queue, tx_queue, control_queue, logger_proc, scan_interval def run(self): pass + def run_total(self): + t_this = StoppableThread( + name=self.Proc_name + '+on_control_rx', + target=self.on_control_rx_init, + args=() + ) + t_this.daemon = self.deamon + t_this.start() + self.run() + + def on_control_rx_init(self): + while True: + if self.Proc_info.control_rx_queue.empty(): + time.sleep(0.02) + else: + try: + packet = self.Proc_info.control_rx_queue.get(block=False) + except: + continue + self.on_control_rx(packet) + + def on_control_rx(self, packet): + #print("!!!! " + self.Proc_name + str(packet.__dict__)) + pass + + def on_terminate(self): + pass + def start(self): - proc_this = multiprocessing.Process(name=self.Proc_name, target=self.run, args=()) + proc_this = multiprocessing.Process(name=self.Proc_name, target=self.run_total, args=()) proc_this.daemon = self.deamon proc_this.start() # self.Proc = proc_this return proc_this def start_lite(self): - proc_this = StoppableThread(name=self.Proc_name, target=self.run, args=()) + proc_this = StoppableThread(name=self.Proc_name, target=self.run_total, args=()) + proc_this.root = self proc_this.daemon = self.deamon proc_this.start() # self.Proc = proc_this diff --git a/OlivOS/L10NDataAPI.py b/OlivOS/L10NDataAPI.py index ad27cc29..064bf78d 100644 --- a/OlivOS/L10NDataAPI.py +++ b/OlivOS/L10NDataAPI.py @@ -93,6 +93,12 @@ 'libCWCBEXEModelAPI_0004': 'OlivOS libCWCBEXEModel failed: {0}\n{1}', 'libCWCBEXEModelAPI_0005': 'OlivOS libCWCBEXEModel server [{0}] is running', 'libCWCBEXEModelAPI_0006': 'OlivOS libCWCBEXEModel server [{0}] exited', + 'bootAPI_0001': 'OlivOS model [{0}] will init', + 'bootAPI_0002': 'OlivOS model [{0}] init', + 'bootAPI_0003': 'OlivOS model [{0}] will try init', + 'bootAPI_0004': 'OlivOS model [{0}] type init', + 'bootAPI_0005': 'OlivOS model [{0}] stopped', + 'bootAPI_0006': 'OlivOS model [{0}] will stop', }, 'zh-CN': { 'diagnoseAPI_0001': '欢迎使用 青果核心交互栈 OlivOS {0}', @@ -166,5 +172,11 @@ 'libCWCBEXEModelAPI_0004': 'OlivOS ComWeChatBotClient进程托管服务组件 错误: {0}\n{1}', 'libCWCBEXEModelAPI_0005': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 正在运作', 'libCWCBEXEModelAPI_0006': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 已经存在', + 'bootAPI_0001': 'OlivOS 组件 [{0}] 即将初始化', + 'bootAPI_0002': 'OlivOS 组件 [{0}] 初始化', + 'bootAPI_0003': 'OlivOS 组件 [{0}] 即将尝试初始化', + 'bootAPI_0004': 'OlivOS 组件 [{0}] 类型 即将初始化', + 'bootAPI_0005': 'OlivOS 组件 [{0}] 已被停止', + 'bootAPI_0006': 'OlivOS 组件 [{0}] 即将停止', } } diff --git a/OlivOS/__init__.py b/OlivOS/__init__.py index 8712ec39..d7746af5 100644 --- a/OlivOS/__init__.py +++ b/OlivOS/__init__.py @@ -68,4 +68,5 @@ from . import libWQEXEModelAPI from . import libCWCBEXEModelAPI from . import nativeWinUIAPI + from . import webviewUIAPI from . import userModule diff --git a/OlivOS/bootAPI.py b/OlivOS/bootAPI.py index 011f67bc..8e3a60a4 100644 --- a/OlivOS/bootAPI.py +++ b/OlivOS/bootAPI.py @@ -28,9 +28,15 @@ import psutil import atexit import importlib +import traceback +import copy import OlivOS +modelName = 'bootAPI' + +gMonitorReg = {} +gLoggerProc = None def releaseDir(dir_path): if not os.path.exists(dir_path): @@ -44,6 +50,7 @@ def __init__(self, basic_conf=None): self.Config['basic_conf_path'] = basic_conf def start(self): + global gLoggerProc # 兼容Win平台多进程,避免形成fork-bomb multiprocessing.freeze_support() atexit.register(killMain) @@ -54,6 +61,7 @@ def start(self): Proc_Proc_dict = {} Proc_logger_name = [] plugin_bot_info_dict = {} + logger_proc = None preLoadPrint('OlivOS - Witness Union') start_up_show_str = (''' @@ -139,6 +147,11 @@ def start(self): if 'auto' == tmp_proc_mode_raw: tmp_proc_mode = 'threading' if basic_conf_models_this['enable']: + logG(1, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] will init', [ + basic_conf_models_this['name'] + ], + modelName + )) if basic_conf_models_this['type'] == 'sleep': time.sleep(10) elif basic_conf_models_this['type'] == 'update_check': @@ -163,6 +176,8 @@ def start(self): basic_conf_models_this['name']].start_unity(tmp_proc_mode) for this_bot_info in plugin_bot_info_dict: plugin_bot_info_dict[this_bot_info].debug_logger = Proc_dict[basic_conf_models_this['name']] + logger_proc = Proc_dict[basic_conf_models_this['name']] + gLoggerProc = Proc_dict[basic_conf_models_this['name']] elif basic_conf_models_this['type'] == 'plugin': proc_plugin_func_dict = {} tmp_tx_queue_list = [] @@ -211,7 +226,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'post': flag_need_enable = False @@ -259,7 +274,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'account_config': plugin_bot_info_dict = OlivOS.accountAPI.Account.load( @@ -315,7 +330,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'discord_link': flag_need_enable = False @@ -337,7 +352,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'kaiheila_link': flag_need_enable = False @@ -359,7 +374,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'biliLive_link': flag_need_enable = False @@ -384,7 +399,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'hackChat_link': flag_need_enable = False @@ -408,7 +423,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'telegram_poll': flag_need_enable = False @@ -468,7 +483,7 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[basic_conf_models_this['name']] = Proc_dict[tmp_Proc_name].start_unity( + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( tmp_proc_mode) elif basic_conf_models_this['type'] == 'dodo_poll': flag_need_enable = False @@ -518,17 +533,35 @@ def start(self): elif basic_conf_models_this['type'] == 'multiLoginUI' and not flag_noblock: if platform.system() == 'Windows': tmp_callbackData = {'res': False} - Proc_dict[basic_conf_models_this['name']] = OlivOS.multiLoginUIAPI.HostUI( + HostUI_obj = OlivOS.multiLoginUIAPI.HostUI( Model_name=basic_conf_models_this['name'], Account_data=plugin_bot_info_dict, logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], callbackData=tmp_callbackData ) - tmp_res = Proc_dict[basic_conf_models_this['name']].start() + tmp_res = HostUI_obj.start() if tmp_res != True: killMain() - if Proc_dict[basic_conf_models_this['name']].UIData['flag_commit']: - plugin_bot_info_dict = Proc_dict[basic_conf_models_this['name']].UIData['Account_data'] + if HostUI_obj.UIData['flag_commit']: + plugin_bot_info_dict = HostUI_obj.UIData['Account_data'] + elif basic_conf_models_this['type'] == 'multiLoginUI_asayc' and not flag_noblock: + if platform.system() == 'Windows': + main_control.control_queue.put( + main_control.packet( + 'send', + { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'account_edit', + 'event': 'account_edit_on', + 'bot_info': plugin_bot_info_dict + } + } + ), + block=False + ) elif basic_conf_models_this['type'] == 'nativeWinUI': if platform.system() == 'Windows': if basic_conf_models_this['name'] not in Proc_dict: @@ -606,6 +639,8 @@ def start(self): time.sleep(Proc_dict[rx_packet_data.key].Proc_info.dead_interval) Proc_Proc_dict[rx_packet_data.key].terminate() Proc_Proc_dict[rx_packet_data.key].join() + Proc_dict.pop(rx_packet_data.key) + Proc_Proc_dict.pop(rx_packet_data.key) elif rx_packet_data.action == 'restart_send': for tmp_Proc_name in basic_conf_models: basic_conf_models_this = basic_conf_models[tmp_Proc_name] @@ -619,9 +654,14 @@ def start(self): if type(rx_packet_data.key) == dict: if 'target' in rx_packet_data.key: if 'type' in rx_packet_data.key['target']: + flag_target_all = (rx_packet_data.key['target']['type'] == 'all') + flag_fliter = 'all' + if 'fliter' in rx_packet_data.key['target']: + flag_fliter = rx_packet_data.key['target']['fliter'] for tmp_Proc_name in basic_conf_models: basic_conf_models_this = basic_conf_models[tmp_Proc_name] - if basic_conf_models_this['type'] == rx_packet_data.key['target']['type']: + + if flag_target_all or basic_conf_models_this['type'] == rx_packet_data.key['target']['type']: model_name = basic_conf_models_this['name'] if 'hash' in rx_packet_data.key['target']: model_name = '%s=%s' % ( @@ -629,11 +669,101 @@ def start(self): rx_packet_data.key['target']['hash'] ) if model_name in Proc_dict: - if Proc_dict[model_name].Proc_info.rx_queue is not None: - Proc_dict[model_name].Proc_info.rx_queue.put( - rx_packet_data, - block=False - ) + if flag_fliter in ['all', 'control_only']: + try: + if Proc_dict[model_name].Proc_info.control_rx_queue is not None: + Proc_dict[model_name].Proc_info.control_rx_queue.put( + rx_packet_data, + block=False + ) + except Exception as e: + traceback.print_exc() + if flag_fliter in ['all', 'rx_only']: + try: + if Proc_dict[model_name].Proc_info.rx_queue is not None: + Proc_dict[model_name].Proc_info.rx_queue.put( + rx_packet_data, + block=False + ) + except Exception as e: + traceback.print_exc() + elif rx_packet_data.action == 'init_type_open_webview_page': + if platform.system() == 'Windows': + if type(rx_packet_data.key) is dict \ + and 'target' in rx_packet_data.key \ + and type(rx_packet_data.key['target']) is dict \ + and 'data' in rx_packet_data.key \ + and type(rx_packet_data.key['data']) is dict \ + and 'action' in rx_packet_data.key['target'] \ + and 'name' in rx_packet_data.key['target'] \ + and 'title' in rx_packet_data.key['data'] \ + and 'url' in rx_packet_data.key['data']: + if 'init' == rx_packet_data.key['target']['action']: + for basic_conf_models_this_key in basic_conf_models: + basic_conf_models_this = basic_conf_models[basic_conf_models_this_key] + if 'webview_page' == basic_conf_models_this['type']: + if basic_conf_models_this['name'] not in Proc_dict: + model_name = '%s+%s' % ( + basic_conf_models_this['name'], + rx_packet_data.key['target']['name'] + ) + Proc_dict[model_name] = OlivOS.webviewUIAPI.page( + Proc_name=model_name, + scan_interval=basic_conf_models_this['interval'], + dead_interval=basic_conf_models_this['dead_interval'], + rx_queue=None, + tx_queue=None, + control_queue=multiprocessing_dict[basic_conf_models_this['control_queue']], + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + title=rx_packet_data.key['data']['title'], + url=rx_packet_data.key['data']['url'] + ) + if model_name not in Proc_Proc_dict: + Proc_Proc_dict[model_name] = Proc_dict[model_name].start_unity('processing') + elif rx_packet_data.action == 'call_system_event': + if type(rx_packet_data.key) is dict \ + and 'action' in rx_packet_data.key \ + and type(rx_packet_data.key['action']) is list: + for event_this in rx_packet_data.key['action']: + if 'event' in basic_conf['system'] \ + and event_this in basic_conf['system']['event']: + for model_this in basic_conf['system']['event'][event_this]: + main_control.control_queue.put( + main_control.packet('init', model_this), + block=False + ) + elif rx_packet_data.action == 'call_system_stop_type_event': + if type(rx_packet_data.key) is dict \ + and 'action' in rx_packet_data.key \ + and type(rx_packet_data.key['action']) is list: + for event_this in rx_packet_data.key['action']: + if 'type_event' in basic_conf['system'] \ + and event_this in basic_conf['system']['type_event']: + for model_this in basic_conf['system']['type_event'][event_this]: + main_control.control_queue.put( + main_control.packet('stop_type', model_this), + block=False + ) + elif rx_packet_data.action == 'call_account_update': + if type(rx_packet_data.key) is dict \ + and 'data' in rx_packet_data.key \ + and type(rx_packet_data.key['data']) is dict: + plugin_bot_info_dict = rx_packet_data.key['data'] + main_control.control_queue.put( + main_control.packet( + 'send', { + 'target': { + 'type': 'all', + 'fliter': 'control_only' + }, + 'data': { + 'action': 'account_update', + 'data': plugin_bot_info_dict + } + } + ), + block=False + ) elif rx_packet_data.action == 'init_type': for tmp_Proc_name in basic_conf_models: basic_conf_models_this = basic_conf_models[tmp_Proc_name] @@ -642,8 +772,49 @@ def start(self): main_control.packet('init', basic_conf_models_this['name']), block=False ) + logG(1, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] type init', [ + tmp_Proc_name + ], + modelName + )) + elif rx_packet_data.action == 'stop_type': + list_stop = [] + for tmp_Proc_name in Proc_Proc_dict: + try: + if tmp_Proc_name in Proc_dict \ + and rx_packet_data.key == Proc_dict[tmp_Proc_name].Proc_type: + Proc_Proc_dict[tmp_Proc_name].terminate() + Proc_Proc_dict[tmp_Proc_name].join() + list_stop.append(tmp_Proc_name) + logG(1, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] will stop', [ + tmp_Proc_name + ], + modelName + )) + except Exception as e: + traceback.print_exc() + for tmp_Proc_name in list_stop: + Proc_dict.pop(tmp_Proc_name) + Proc_Proc_dict.pop(tmp_Proc_name) + elif rx_packet_data.action == 'stop': + tmp_Proc_name = rx_packet_data.key + try: + if tmp_Proc_name in Proc_Proc_dict: + Proc_Proc_dict[tmp_Proc_name].terminate() + Proc_Proc_dict[tmp_Proc_name].join() + logG(1, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] will stop', [ + tmp_Proc_name + ], + modelName + )) + Proc_Proc_dict.pop(tmp_Proc_name) + if tmp_Proc_name in Proc_dict: + Proc_dict.pop(tmp_Proc_name) + except Exception as e: + traceback.print_exc() elif rx_packet_data.action == 'exit_total': killMain() + bootMonitor(varDict=locals()) def update_get_func( @@ -664,12 +835,44 @@ def update_get_func( block=False ) +# 进程监控 +def bootMonitor(varDict:dict): + global gMonitorReg + try: + gMonitorReg.setdefault('Proc_dict', {'keys': []}) + if 'Proc_dict' in varDict \ + and type(varDict['Proc_dict']) is dict: + flagNeedRefresh = False + for Proc_name in gMonitorReg['Proc_dict']['keys']: + if Proc_name not in varDict['Proc_dict']: + flagNeedRefresh = True + logG(2, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] stopped', [ + Proc_name + ], + modelName + )) + for Proc_name in varDict['Proc_dict']: + if Proc_name not in gMonitorReg['Proc_dict']['keys']: + flagNeedRefresh = True + logG(2, OlivOS.L10NAPI.getTrans('OlivOS model [{0}] init', [ + Proc_name + ], + modelName + )) + if flagNeedRefresh: + gMonitorReg['Proc_dict']['keys'] = copy.deepcopy(list(varDict['Proc_dict'].keys())) + except Exception as e: + traceback.print_exc() + +# 进程管理 +def killByPid(pid): + parent = psutil.Process(pid) + kill_process_and_its_children(parent) def killMain(): parent = psutil.Process(os.getpid()) kill_process_and_its_children(parent) - def kill_process(p): try: p.terminate() @@ -698,6 +901,24 @@ def kill_process_children(p): else: kill_process(child) +# 日志工具 +def log(logger_proc, log_level, log_message, log_segment=None): + if log_segment is None: + log_segment = [] + try: + if logger_proc is not None: + logger_proc.log(log_level, log_message, log_segment) + except Exception as e: + traceback.print_exc() + +def logG(log_level, log_message, log_segment=None): + global gLoggerProc + log( + logger_proc=gLoggerProc, + log_level=log_level, + log_message=log_message, + log_segment=log_segment + ) # 启动画面的操作 def setSplashClose(): @@ -705,11 +926,11 @@ def setSplashClose(): import pyi_splash pyi_splash.close() -def setSplashText(text:str): +def setSplashText(msg:str): if '_PYIBoot_SPLASH' in os.environ and importlib.util.find_spec("pyi_splash"): import pyi_splash - pyi_splash.update_text(text) + pyi_splash.update_text(msg) -def preLoadPrint(text:str): - print(text) +def preLoadPrint(msg:str): + print(msg) #setSplashText(text) diff --git a/OlivOS/bootDataAPI.py b/OlivOS/bootDataAPI.py index 441f5215..0929a453 100644 --- a/OlivOS/bootDataAPI.py +++ b/OlivOS/bootDataAPI.py @@ -33,11 +33,10 @@ "OlivOS_walleq_lib_exe_model", "OlivOS_cwcb_lib_exe_model", "OlivOS_hackChat_link", - "OlivOS_account_config_safe", "OlivOS_plugin", "OlivOS_virtual_terminal_link", "OlivOS_flask_post_rx", - "OlivOS_onebotv12_link", + "OlivOS_onebotV12_link", "OlivOS_qqGuild_link", "OlivOS_discord_link", "OlivOS_telegram_poll", @@ -47,6 +46,62 @@ "OlivOS_biliLive_link", "OlivOS_update_check" ], + "event": { + "account_edit": [ + "OlivOS_account_config", + "OlivOS_multiLoginUI", + "OlivOS_account_fix", + "OlivOS_account_config_save", + "OlivOS_account_config", + "OlivOS_account_config_update" + ], + "account_edit_asayc_start": [ + "OlivOS_account_config" + ], + "account_edit_asayc_do": [ + "OlivOS_multiLoginUI_asayc", + ], + "account_edit_asayc_end": [ + "OlivOS_account_fix", + "OlivOS_account_config_save", + "OlivOS_account_config", + "OlivOS_account_config_update" + ], + "account_update": [ + "OlivOS_gocqhttp_lib_exe_model", + "OlivOS_walleq_lib_exe_model", + "OlivOS_cwcb_lib_exe_model", + "OlivOS_hackChat_link", + "OlivOS_virtual_terminal_link", + "OlivOS_flask_post_rx", + "OlivOS_onebotV12_link", + "OlivOS_qqGuild_link", + "OlivOS_discord_link", + "OlivOS_telegram_poll", + "OlivOS_fanbook_poll", + "OlivOS_kaiheila_link", + "OlivOS_dodo_link", + "OlivOS_biliLive_link", + ] + }, + "type_event": { + "account_update": [ + "gocqhttp_lib_exe_model", + "walleq_lib_exe_model", + "cwcb_lib_exe_model", + "hackChat_link", + "terminal_link", + "flask_post_rx", + "onebotV12_link", + "qqGuild_link", + "discord_link", + "telegram_poll", + "fanbook_poll", + "kaiheila_link", + "dodo_link", + "biliLive_link", + ] + }, "control_queue": "OlivOS_control_queue", "interval": 0.2, "proc_mode": "auto" @@ -84,6 +139,12 @@ "type": "multiLoginUI", "logger_proc": "OlivOS_logger" }, + "OlivOS_multiLoginUI_asayc": { + "enable": True, + "name": "OlivOS_multiLoginUI_asayc", + "type": "multiLoginUI_asayc", + "logger_proc": "OlivOS_logger" + }, "OlivOS_nativeWinUIAPI": { "enable": True, "name": "OlivOS_nativeWinUIAPI", @@ -130,6 +191,15 @@ "path": "./conf/account.json" } }, + "OlivOS_account_config_update": { + "enable": True, + "name": "OlivOS_account_config_update", + "type": "account_config_update", + "logger_proc": "OlivOS_logger", + "data": { + "path": "./conf/account.json" + } + }, "OlivOS_logger": { "enable": True, "name": "OlivOS_logger", @@ -190,9 +260,9 @@ "port": 55001 } }, - "OlivOS_onebotv12_link": { + "OlivOS_onebotV12_link": { "enable": True, - "name": "OlivOS_onebotv12_link", + "name": "OlivOS_onebotV12_link", "type": "onebotV12_link", "interval": 0.002, "dead_interval": 1, @@ -357,6 +427,16 @@ "control_queue": "OlivOS_control_queue", "debug": False }, + "OlivOS_webview_page": { + "enable": True, + "name": "OlivOS_webview_page", + "type": "webview_page", + "interval": 0.2, + "dead_interval": 1, + "logger_proc": "OlivOS_logger", + "control_queue": "OlivOS_control_queue", + "debug": False + }, "OlivOS_update_get": { "enable": True, "name": "OlivOS_update_get", diff --git a/OlivOS/hook.py b/OlivOS/hook.py index 10ef9cc0..c7e11e7c 100644 --- a/OlivOS/hook.py +++ b/OlivOS/hook.py @@ -26,3 +26,4 @@ if platform.system() == 'Windows': import win32com.client import pythoncom + import webview diff --git a/OlivOS/hook_pack.py b/OlivOS/hook_pack.py index a1b7e681..44f1683b 100644 --- a/OlivOS/hook_pack.py +++ b/OlivOS/hook_pack.py @@ -26,6 +26,7 @@ if platform.system() == 'Windows': import win32com.client import pythoncom + import webview # ext pack # lxml @@ -49,3 +50,18 @@ import prompt_toolkit import regex import rich + +import sys + +# Are we running in a PyInstaller bundle +# https://pyinstaller.org/en/stable/runtime-information.html#run-time-information +if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + class NullOutput(object): + def write(self, string): + pass + + def isatty(self): + return False + + sys.stdout = NullOutput() + sys.stderr = NullOutput() diff --git a/OlivOS/hook_pack_debug.py b/OlivOS/hook_pack_debug.py new file mode 100644 index 00000000..5d4c21b9 --- /dev/null +++ b/OlivOS/hook_pack_debug.py @@ -0,0 +1,54 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/hook.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import platform + +# pillow +from PIL import Image + +# sqlite +import sqlite3 + +# win +if platform.system() == 'Windows': + import win32com.client + import pythoncom + import webview + +# ext pack +# lxml +from lxml import etree + +# yaml +import yaml + +# openpyxl +import openpyxl + +# aiohttp +import aiohttp + +# qrcode +import qrcode + +# weblib +import certifi +import httpx +import prompt_toolkit +import regex +import rich + +import sys diff --git a/OlivOS/libCWCBEXEModelAPI.py b/OlivOS/libCWCBEXEModelAPI.py index 937799c3..72e135c1 100644 --- a/OlivOS/libCWCBEXEModelAPI.py +++ b/OlivOS/libCWCBEXEModelAPI.py @@ -102,7 +102,7 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non OlivOS.API.Proc_templet.__init__( self, Proc_name=Proc_name, - Proc_type='lib_cwcb_exe_model', + Proc_type='cwcb_lib_exe_model', scan_interval=scan_interval, dead_interval=dead_interval, rx_queue=rx_queue, @@ -115,14 +115,15 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non self.Proc_config['target_proc'] = target_proc self.Proc_data['check_qrcode_flag'] = False self.Proc_data['check_stdin'] = False + self.Proc_data['model_Proc'] = None + self.flag_run = True def run(self): - flag_run = True if self.Proc_data['bot_info_dict'].platform['model'] in [ 'ComWeChatBotClient' ]: self.send_init_event() - while flag_run: + while self.flag_run: releaseDir('./lib') if not os.path.exists('./lib/ComWeChat-Client.exe'): self.log(3, OlivOS.L10NAPI.getTrans( @@ -174,9 +175,11 @@ def run(self): creationflags=subprocess.CREATE_NEW_CONSOLE, env=tmp_env ) + self.Proc_data['model_Proc'] = model_Proc threading.Thread( target=self.check_stdin, - args=(model_Proc,) + args=(model_Proc,), + daemon=self.deamon ).start() self.get_model_stdout(model_Proc) # model_Proc.communicate(timeout = None) @@ -184,8 +187,15 @@ def run(self): 'OlivOS libCWCBEXEModel server [{0}] will retry in 10s...', [self.Proc_name], modelName )) + self.Proc_data['model_Proc'] = None time.sleep(8) + def on_terminate(self): + self.flag_run = False + if 'model_Proc' in self.Proc_data \ + and self.Proc_data['model_Proc'] is not None: + OlivOS.bootAPI.killByPid(self.Proc_data['model_Proc'].pid) + def getBotIDStr(self): tmp_self_data = self.Proc_data['bot_info_dict'].platform['platform'] if self.Proc_data['bot_info_dict'].id is not None: diff --git a/OlivOS/libEXEModelAPI.py b/OlivOS/libEXEModelAPI.py index 93e74c59..4e108c68 100644 --- a/OlivOS/libEXEModelAPI.py +++ b/OlivOS/libEXEModelAPI.py @@ -107,9 +107,10 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non self.Proc_config['target_proc'] = target_proc self.Proc_data['check_qrcode_flag'] = False self.Proc_data['check_stdin'] = False + self.Proc_data['model_Proc'] = None + self.flag_run = True def run(self): - flag_run = True if self.Proc_data['bot_info_dict'].platform['model'] in [ 'gocqhttp_show', 'gocqhttp_show_Android_Phone', @@ -119,7 +120,7 @@ def run(self): 'gocqhttp_show_Android_Pad' ]: self.send_init_event() - while flag_run: + while self.flag_run: releaseDir('./lib') if not os.path.exists('./lib/go-cqhttp.exe'): self.log(3, OlivOS.L10NAPI.getTrans( @@ -175,7 +176,8 @@ def run(self): self.Proc_data['check_stdin'] = True threading.Thread( target=self.check_qrcode, - args=() + args=(), + daemon=self.deamon ).start() tmp_env = dict(os.environ) # 依据 https://github.com/Mrs4s/go-cqhttp/pull/1772 的改动 @@ -190,9 +192,11 @@ def run(self): creationflags=subprocess.CREATE_NEW_CONSOLE, env=tmp_env ) + self.Proc_data['model_Proc'] = model_Proc threading.Thread( target=self.check_stdin, - args=(model_Proc,) + args=(model_Proc,), + daemon=self.deamon ).start() self.get_model_stdout(model_Proc) # model_Proc.communicate(timeout = None) @@ -200,6 +204,7 @@ def run(self): 'OlivOS libEXEModel server [{0}] will retry in 10s...', [self.Proc_name], modelName )) + self.Proc_data['model_Proc'] = None time.sleep(8) elif self.Proc_data['bot_info_dict'].platform['model'] in [ 'gocqhttp_show_old' @@ -216,7 +221,13 @@ def run(self): cwd='.\\conf\\gocqhttp\\' + self.Proc_data['bot_info_dict'].hash, env=tmp_env ) - flag_run = False + self.flag_run = False + + def on_terminate(self): + self.flag_run = False + if 'model_Proc' in self.Proc_data \ + and self.Proc_data['model_Proc'] is not None: + OlivOS.bootAPI.killByPid(self.Proc_data['model_Proc'].pid) def getBotIDStr(self): tmp_self_data = self.Proc_data['bot_info_dict'].platform['platform'] diff --git a/OlivOS/libWQEXEModelAPI.py b/OlivOS/libWQEXEModelAPI.py index 43cfbfce..e96a7c14 100644 --- a/OlivOS/libWQEXEModelAPI.py +++ b/OlivOS/libWQEXEModelAPI.py @@ -93,7 +93,7 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non OlivOS.API.Proc_templet.__init__( self, Proc_name=Proc_name, - Proc_type='lib_wq_exe_model', + Proc_type='walleq_lib_exe_model', scan_interval=scan_interval, dead_interval=dead_interval, rx_queue=rx_queue, @@ -106,9 +106,10 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non self.Proc_config['target_proc'] = target_proc self.Proc_data['check_qrcode_flag'] = False self.Proc_data['check_stdin'] = False + self.Proc_data['model_Proc'] = None + self.flag_run = True def run(self): - flag_run = True if self.Proc_data['bot_info_dict'].platform['model'] in [ 'walleq_show', 'walleq_show_Android_Phone', @@ -118,7 +119,7 @@ def run(self): 'walleq_show_Android_Pad' ]: self.send_init_event() - while flag_run: + while self.flag_run: releaseDir('./lib') if not os.path.exists('./lib/walle-q.exe'): self.log(3, OlivOS.L10NAPI.getTrans( @@ -151,7 +152,8 @@ def run(self): self.Proc_data['check_stdin'] = True threading.Thread( target=self.check_qrcode, - args=() + args=(), + daemon=self.deamon ).start() tmp_env = dict(os.environ) model_Proc = subprocess.Popen( @@ -164,9 +166,11 @@ def run(self): creationflags=subprocess.CREATE_NEW_CONSOLE, env=tmp_env ) + self.Proc_data['model_Proc'] = model_Proc threading.Thread( target=self.check_stdin, - args=(model_Proc,) + args=(model_Proc,), + daemon=self.deamon ).start() self.get_model_stdout(model_Proc) # model_Proc.communicate(timeout = None) @@ -174,6 +178,7 @@ def run(self): 'OlivOS libWQEXEModel server [{0}] will retry in 10s...', [self.Proc_name], modelName )) + self.Proc_data['model_Proc'] = None time.sleep(8) elif self.Proc_data['bot_info_dict'].platform['model'] in [ 'walleq_show_old' @@ -189,7 +194,13 @@ def run(self): cwd='.\\conf\\walleq\\' + self.Proc_data['bot_info_dict'].hash, env=tmp_env ) - flag_run = False + self.flag_run = False + + def on_terminate(self): + self.flag_run = False + if 'model_Proc' in self.Proc_data \ + and self.Proc_data['model_Proc'] is not None: + OlivOS.bootAPI.killByPid(self.Proc_data['model_Proc'].pid) def getBotIDStr(self): tmp_self_data = self.Proc_data['bot_info_dict'].platform['platform'] @@ -359,7 +370,6 @@ def setConfig(self): self.config_file_format['password'] = '' if self.bot_info_dict.password != '': self.config_file_format['password'] = 'password = "%s"' % self.bot_info_dict.password - self.config_file_format['password'] = self.bot_info_dict.password self.config_file_format['token'] = self.bot_info_dict.post_info.access_token self.config_file_format['port'] = str(self.bot_info_dict.post_info.port) self.config_file_format['host'] = '127.0.0.1' diff --git a/OlivOS/multiLoginUIAPI.py b/OlivOS/multiLoginUIAPI.py index ec9593f3..753b7c6e 100644 --- a/OlivOS/multiLoginUIAPI.py +++ b/OlivOS/multiLoginUIAPI.py @@ -20,6 +20,8 @@ import hashlib import random import shutil +import platform +import traceback from tkinter import ttk from tkinter import messagebox @@ -35,15 +37,92 @@ 'color_006': '#80D7FF' } +def run_HostUI_asayc(plugin_bot_info_dict, control_queue): + if platform.system() == 'Windows': + try: + tmp_callbackData = {'res': False} + tmp_t = OlivOS.multiLoginUIAPI.HostUI( + Model_name='OlivOS_multiLoginUI_asayc', + Account_data=plugin_bot_info_dict, + logger_proc=None, + callbackData=tmp_callbackData, + asaycMode=True, + rootMode=True, + control_queue=control_queue + ) + tmp_res = tmp_t.start() + if tmp_res != True: + pass + if tmp_t.UIData['flag_commit']: + pass + except Exception as e: + traceback.print_exc() + +def sendAccountUpdate(obj, control_queue, account_data): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet( + 'call_system_event', + { + 'action': [ + 'account_edit_asayc_end' + ] + } + ), + block=False + ) + control_queue.put( + OlivOS.API.Control.packet( + 'call_account_update', + { + 'data': account_data + } + ), + block=False + ) + control_queue.put( + OlivOS.API.Control.packet( + 'call_system_stop_type_event', + { + 'action': [ + 'account_update' + ] + } + ), + block=False + ) + control_queue.put( + OlivOS.API.Control.packet( + 'call_system_event', + { + 'action': [ + 'account_update' + ] + } + ), + block=False + ) class HostUI(object): - def __init__(self, Model_name, Account_data, logger_proc=None, callbackData=None): + def __init__( + self, + Model_name, + Account_data, + logger_proc=None, + callbackData=None, + asaycMode=False, + rootMode=True, + control_queue=None + ): self.Model_name = Model_name self.UIObject = {} self.UIData = {} self.UIConfig = {} self.logger_proc = logger_proc self.callbackData = callbackData + self.asaycMode = asaycMode + self.rootMode = rootMode + self.control_queue = control_queue self.res = False self.UIData['Account_data'] = Account_data self.UIData['flag_commit'] = False @@ -55,7 +134,10 @@ def log(self, log_level, log_message): self.logger_proc.log(log_level, log_message) def start(self): - self.UIObject['root'] = tkinter.Tk() + if self.rootMode: + self.UIObject['root'] = tkinter.Tk() + else: + self.UIObject['root'] = tkinter.Toplevel() self.UIObject['root'].title('OlivOS 登录管理器') self.UIObject['root'].geometry('518x400') self.UIObject['root'].resizable( @@ -227,6 +309,7 @@ def account_data_commit(self): self.res = True if type(self.callbackData) == dict: self.callbackData['res'] = self.res + sendAccountUpdate(self, self.control_queue, self.UIData['Account_data']) self.UIObject['root'].destroy() diff --git a/OlivOS/multiprocessing_win.py b/OlivOS/multiprocessing_win.py deleted file mode 100644 index bcdda146..00000000 --- a/OlivOS/multiprocessing_win.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- encoding: utf-8 -*- -''' -_______________________ ________________ -__ __ \__ /____ _/_ | / /_ __ \_ ___/ -_ / / /_ / __ / __ | / /_ / / /____ \ -/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / -\____/ /_____/___/ _____/ \____/ /____/ - -@File : OlivOS/multiprocessing_win.py -@Author : lunzhiPenxil仑质 -@Contact : lunzhipenxil@gmail.com -@License : AGPL -@Copyright : (C) 2020-2023, OlivOS-Team -@Desc : None -''' - -import os -import sys -import multiprocessing - -try: - if sys.platform.startswith('win'): - import multiprocessing.popen_spawn_win32 as forking - else: - import multiprocessing.popen_fork as forking -except ImportError: - import multiprocessing.forking as forking - -if sys.platform.startswith('win'): - class _Popen(forking.Popen): - def __init__(self, *args, **kw): - if hasattr(sys, 'frozen'): - os.putenv('_MEIPASS2', sys._MEIPASS) - try: - super(_Popen, self).__init__(*args, **kw) - finally: - if hasattr(sys, 'frozen'): - if hasattr(os, 'unsetenv'): - os.unsetenv('_MEIPASS2') - else: - os.putenv('_MEIPASS2', '') - forking.Popen = _Popen diff --git a/OlivOS/nativeWinUIAPI.py b/OlivOS/nativeWinUIAPI.py index d380afd1..479e6b5d 100644 --- a/OlivOS/nativeWinUIAPI.py +++ b/OlivOS/nativeWinUIAPI.py @@ -42,25 +42,6 @@ 'color_006': '#80D7FF' } - -class StoppableThread(threading.Thread): - def __init__(self, *args, **kwargs): - super(StoppableThread, self).__init__(*args, **kwargs) - self._stop_event = threading.Event() - - def terminate(self): - self._stop_event.set() - - def stop(self): - self._stop_event.set() - - def join(self): - pass - - def stopped(self): - return self._stop_event.is_set() - - class dock(OlivOS.API.Proc_templet): def __init__( self, @@ -127,6 +108,23 @@ def run(self): self.process_msg() self.UIObject['main_tk'].mainloop() + def on_control_rx(self, packet): + if type(packet) is OlivOS.API.Control.packet: + if 'send' == packet.action: + if type(packet.key) is dict \ + and 'data' in packet.key \ + and type(packet.key['data']) \ + and 'action' in packet.key['data']: + if 'account_update' == packet.key['data']['action']: + if 'data' in packet.key['data'] \ + and type(packet.key['data']['data']) is dict: + self.bot_info = packet.key['data']['data'] + self.UIData['shallow_gocqhttp_menu_list'] = None + self.UIData['shallow_walleq_menu_list'] = None + self.UIData['shallow_cwcb_menu_list'] = None + self.UIData['shallow_virtual_terminal_menu_list'] = None + self.updateShallowMenuList() + def process_msg(self): self.UIObject['main_tk'].after(50, self.process_msg) self.mainrun() @@ -156,6 +154,15 @@ def mainrun(self): else: self.updateShallow() self.updatePluginEdit() + elif 'account_edit' == rx_packet_data.key['data']['action']: + if 'event' in rx_packet_data.key['data'] \ + and 'account_edit_on' == rx_packet_data.key['data']['event'] \ + and 'bot_info' in rx_packet_data.key['data'] \ + and type(rx_packet_data.key['data']['bot_info']) is dict: + OlivOS.multiLoginUIAPI.run_HostUI_asayc( + plugin_bot_info_dict=rx_packet_data.key['data']['bot_info'], + control_queue=self.Proc_info.control_queue + ) elif 'plugin_edit_menu_on' == rx_packet_data.key['data']['action']: self.startPluginEdit() elif 'logger' == rx_packet_data.key['data']['action']: @@ -376,8 +383,8 @@ def mainrun(self): def updateShallowMenuList(self): tmp_new = [] self.UIData['shallow_menu_list'] = [ - ['账号管理', False], ['打开终端', self.startOlivOSTerminalUISend], + ['账号管理', self.startAccountEditSendFunc()], ['gocqhttp管理', self.UIData['shallow_gocqhttp_menu_list']], ['walleq管理', self.UIData['shallow_walleq_menu_list']], ['ComWeChat管理', self.UIData['shallow_cwcb_menu_list']], @@ -385,6 +392,7 @@ def updateShallowMenuList(self): ['插件管理', self.startPluginEditSend], ['插件菜单', self.UIData['shallow_plugin_menu_list']], ['重载插件', self.sendPluginRestart], + ['社区论坛', self.sendOpenForum], ['更新OlivOS', self.sendOlivOSUpdateGet], ['退出OlivOS', self.setOlivOSExit] ] @@ -396,6 +404,21 @@ def updateShallowMenuList(self): tmp_new.append(data_this) self.UIData['shallow_menu_list'] = tmp_new + def startAccountEditSendFunc(self): + def resFunc(): + self.startAccountEditSend() + return resFunc + + def startAccountEditSend(self): + self.sendControlEventSend( + 'call_system_event', { + 'action': [ + 'account_edit_asayc_start', + 'account_edit_asayc_do' + ] + } + ) + def startGoCqhttpTerminalUISendFunc(self, hash): def resFunc(): self.startGoCqhttpTerminalUISend(hash) @@ -722,6 +745,26 @@ def sendOlivOSUpdateGet(self): block=False ) + def sendOpenForum(self): + if self.UIObject['root_shallow'] is not None: + self.UIObject['root_shallow'].UIObject['shallow_root'].notify( + '正在前往社区论坛……' + ) + self.sendOpenWebviewEvent('forum_page', 'OlivOS论坛', 'https://forum.olivos.run/') + + def sendOpenWebviewEvent( + self, + name:str, + title:str, + url:str + ): + OlivOS.webviewUIAPI.sendOpenWebviewPage( + self.Proc_info.control_queue, + name, + title, + url + ) + def startShallowSend(self): self.sendRxEvent('send', { 'target': { @@ -987,7 +1030,16 @@ def show_url_webbrowser(self, url): res = tkinter.messagebox.askquestion("请完成验证", "是否通过浏览器访问 \"" + url + "\" ?") try: if res == 'yes': - webbrowser.open(url) + res = tkinter.messagebox.askquestion("请完成验证", "是否使用内置浏览器?") + if res == 'yes': + OlivOS.webviewUIAPI.sendOpenWebviewPage( + control_queue=self.root.Proc_info.control_queue, + name='slider_verification_code=%s' % self.bot.hash, + title='请完成验证', + url=url + ) + else: + webbrowser.open(url) except webbrowser.Error as error_info: tkinter.messagebox.showerror("webbrowser.Error", error_info) diff --git a/OlivOS/pluginAPI.py b/OlivOS/pluginAPI.py index 6afb044e..bede0355 100644 --- a/OlivOS/pluginAPI.py +++ b/OlivOS/pluginAPI.py @@ -169,6 +169,16 @@ def run(self): if rx_count == self.Proc_config['step_to_restart']: self.set_restart() + def on_control_rx(self, packet): + if type(packet) is OlivOS.API.Control.packet: + if 'send' == packet.action: + if type(packet.key) is dict \ + and 'data' in packet.key \ + and type(packet.key['data']) \ + and 'action' in packet.key['data']: + if 'account_update' == packet.key['data']['action']: + self.set_restart() + def set_restart(self): self.log(2, OlivOS.L10NAPI.getTrans( 'OlivOS plugin shallow [{0}] call restart', [ @@ -349,6 +359,43 @@ def check_plugin_list(self): )) self.plugin_models_call_list = new_list + def run_plugin_data_release(self): + for plugin_models_index_this in self.plugin_models_call_list: + if plugin_models_index_this in self.plugin_models_dict: + self.run_plugin_data_release_by_name(plugin_models_index_this) + + def run_plugin_data_release_by_name(self, plugin_models_index_this): + func_name = 'release_data' + dataPath = './plugin/data/%s/data' % plugin_models_index_this + dataPathFromList = [ + './plugin/app/%s/data' % plugin_models_index_this, + './plugin/tmp/%s/data' % plugin_models_index_this + ] + for dataPathFrom in dataPathFromList: + if os.path.exists(dataPathFrom) \ + and os.path.isdir(dataPathFrom): + try: + removeDir(dataPath) + shutil.copytree(dataPathFrom, dataPath) + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS plugin [{0}] call [{1}] done', [ + self.plugin_models_dict[plugin_models_index_this]['name'], + func_name + ], + modelName + )) + except Exception as e: + self.log(4, OlivOS.L10NAPI.getTrans( + 'OlivOS plugin [{0}] call [{1}] failed: {2}\n{3}', [ + self.plugin_models_dict[plugin_models_index_this]['name'], + func_name, + str(e), + traceback.format_exc() + ], + modelName + )) + break + def run_plugin_func(self, plugin_event, func_name): for plugin_models_index_this in self.plugin_models_call_list: if plugin_models_index_this in self.plugin_models_dict: @@ -673,6 +720,7 @@ def load_plugin_list(self): modelName )) # doOpkRemove(plugin_path_tmp, plugin_dir_this_tmp) + self.run_plugin_data_release_by_name(plugin_models_dict_this_key) continue else: skip_result = plugin_dir_this + '.main.Event' + ' not found' diff --git a/OlivOS/webviewUIAPI.py b/OlivOS/webviewUIAPI.py new file mode 100644 index 00000000..37c65801 --- /dev/null +++ b/OlivOS/webviewUIAPI.py @@ -0,0 +1,89 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/webviewUIAPI.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import OlivOS + +import webview +import time +import multiprocessing + +class page(OlivOS.API.Proc_templet): + def __init__( + self, + Proc_name='webview_page', + scan_interval=0.001, + dead_interval=1, + rx_queue=None, + tx_queue=None, + logger_proc=None, + control_queue=None, + title='OlivOS Page', + url=None + ): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='webview_page', + scan_interval=scan_interval, + dead_interval=dead_interval, + rx_queue=rx_queue, + tx_queue=tx_queue, + control_queue=control_queue, + logger_proc=logger_proc + ) + self.UIObject = {} + self.UIData = { + 'title': title, + 'url': url + } + + def run(self): + if self.UIData['url'] != None: + webview.create_window(self.UIData['title'], self.UIData['url']) + webview.start() + + # 发送并等待结束 + if self.Proc_info.control_queue is not None: + self.Proc_info.control_queue.put( + OlivOS.API.Control.packet('stop', self.Proc_name), + block=False + ) + else: + pass + +def sendOpenWebviewPage( + control_queue, + name:str, + title:str, + url:str +): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet( + 'init_type_open_webview_page', + { + 'target': { + 'action': 'init', + 'name': name + }, + 'data': { + 'title': title, + 'url': url + } + } + ), + block=False + ) diff --git a/requirements310_win.txt b/requirements310_win.txt index 11cd7d59..edce992c 100644 --- a/requirements310_win.txt +++ b/requirements310_win.txt @@ -25,3 +25,4 @@ httpx prompt_toolkit regex rich +pywebview diff --git a/requirements_win.txt b/requirements_win.txt index 26b411f4..80b74884 100644 --- a/requirements_win.txt +++ b/requirements_win.txt @@ -25,3 +25,4 @@ httpx prompt_toolkit regex rich +pywebview