diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 9c8d3c05..3b6692b5 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ # These are supported funding model platforms -custom: ['https://afdian.net/@OlivOS'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +custom: ['https://afdian.com/a/OlivOS'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0041fb85 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/OlivOS/__init__.py b/OlivOS/__init__.py index 86b36715..c62a791f 100644 --- a/OlivOS/__init__.py +++ b/OlivOS/__init__.py @@ -18,63 +18,69 @@ import platform -from . import infoAPI -from . import L10NAPI -from . import L10NDataAPI -from . import bootAPI -from . import bootDataAPI -from . import data +from .core.info import infoAPI +from .core.L10N import L10NAPI +from .core.L10N import L10NDataAPI +from .core.boot import bootAPI +from .core.boot import bootDataAPI +from .core.inlineData import data from . import hook -from . import contentAPI -from . import messageAPI -from . import metadataAPI -from . import API +from .core.core import contentAPI +from .core.core import messageAPI +from .core.core import metadataAPI +from .core.core import API from . import thirdPartyModule -from . import accountAPI -from . import diagnoseAPI -from . import flaskServerAPI -from . import onebotV12SDK -from . import onebotV12LinkServerAPI -from . import pluginAPI -from . import onebotSDK -from . import virtualTerminalSDK -from . import virtualTerminalLinkServerAPI -from . import qqGuildSDK -from . import qqGuildLinkServerAPI -from . import qqGuildv2SDK -from . import qqGuildv2LinkServerAPI -from . import qqRedSDK -from . import qqRedLinkServerAPI -from . import telegramSDK -from . import telegramPollServerAPI -from . import discordSDK -from . import discordLinkServerAPI -from . import hackChatSDK -from . import hackChatLinkServerAPI -from . import dodobotEAServerAPI -from . import dodobotEATXAPI -from . import dodobotEASDK -from . import dodoSDK -from . import dodoPollServerAPI -from . import dodoLinkSDK -from . import dodoLinkServerAPI -from . import dingtalkSDK -from . import dingtalkLinkServerAPI -from . import fanbookSDK -from . import fanbookPollServerAPI -from . import kaiheilaSDK -from . import kaiheilaLinkServerAPI -from . import mhyVilaSDK -from . import mhyVilaLinkServerAPI -from . import biliLiveSDK -from . import biliLiveLinkServerAPI -from . import updateAPI -from . import webTool +from .core.core import accountAPI +from .core.core import accountMetadataAPI +from .core.core import diagnoseAPI +from .adapter.onebotV11 import flaskServerAPI +from .adapter.onebotV12 import onebotV12SDK +from .adapter.onebotV12 import onebotV12LinkServerAPI +from .core.core import pluginAPI +from .adapter.onebotV11 import onebotSDK +from .adapter.virtualTerminal import virtualTerminalSDK +from .adapter.virtualTerminal import virtualTerminalLinkServerAPI +from .adapter.qqGuild import qqGuildSDK +from .adapter.qqGuild import qqGuildLinkServerAPI +from .adapter.qqGuild import qqGuildv2SDK +from .adapter.qqGuild import qqGuildv2LinkServerAPI +from .adapter.red import qqRedSDK +from .adapter.red import qqRedLinkServerAPI +from .adapter.OPQBot import OPQBotSDK +from .adapter.OPQBot import OPQBotLinkServerAPI +from .adapter.telegram import telegramSDK +from .adapter.telegram import telegramPollServerAPI +from .adapter.discord import discordSDK +from .adapter.discord import discordLinkServerAPI +from .adapter.hackChat import hackChatSDK +from .adapter.hackChat import hackChatLinkServerAPI +from .adapter.dodo import dodobotEAServerAPI +from .adapter.dodo import dodobotEATXAPI +from .adapter.dodo import dodobotEASDK +from .adapter.dodo import dodoSDK +from .adapter.dodo import dodoPollServerAPI +from .adapter.dodo import dodoLinkSDK +from .adapter.dodo import dodoLinkServerAPI +from .adapter.dingtalk import dingtalkSDK +from .adapter.dingtalk import dingtalkLinkServerAPI +from .adapter.fanbook import fanbookSDK +from .adapter.fanbook import fanbookPollServerAPI +from .adapter.kaiheila import kaiheilaSDK +from .adapter.kaiheila import kaiheilaLinkServerAPI +from .adapter.mhyVila import mhyVilaSDK +from .adapter.mhyVila import mhyVilaLinkServerAPI +from .adapter.biliLive import biliLiveSDK +from .adapter.biliLive import biliLiveLinkServerAPI +from .core.web import updateAPI +from .core.web import webTool if platform.system() == 'Windows': - from . import multiLoginUIAPI - from . import libEXEModelAPI - from . import libWQEXEModelAPI - from . import libCWCBEXEModelAPI - from . import nativeWinUIAPI - from . import webviewUIAPI + from .nativeGUI import multiLoginUIAPI + from .libBooter import libEXEModelAPI + from .libBooter import libWQEXEModelAPI + from .libBooter import libCWCBEXEModelAPI + from .libBooter import libOPQBotEXEModelAPI + from .libBooter import libNapCatEXEModelAPI + from .libBooter import libAstralQsignEXEModelAPI + from .nativeGUI import nativeWinUIAPI + from .nativeGUI import webviewUIAPI from . import userModule diff --git a/OlivOS/adapter/OPQBot/OPQBotLinkServerAPI.py b/OlivOS/adapter/OPQBot/OPQBotLinkServerAPI.py new file mode 100644 index 00000000..57ff194f --- /dev/null +++ b/OlivOS/adapter/OPQBot/OPQBotLinkServerAPI.py @@ -0,0 +1,140 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/OPQBotLinkServerAPI.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import time +import json +import websocket +import uuid +import threading + +import OlivOS + +gCheckList = [ + 'opqbot_default', + 'opqbot_auto', + 'opqbot_port', + 'opqbot_port_old', +] + +modelName = 'OPQBotLinkServerAPI' + +class server(OlivOS.API.Proc_templet): + def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=None, tx_queue=None, logger_proc=None, + debug_mode=False, bot_info_dict=None): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='OPQBot_link', + scan_interval=scan_interval, + dead_interval=dead_interval, + rx_queue=rx_queue, + tx_queue=tx_queue, + logger_proc=logger_proc + ) + self.Proc_config['debug_mode'] = debug_mode + self.Proc_data['bot_info_dict'] = bot_info_dict + self.Proc_data['extend_data'] = { + 'websocket_url': None, + 'ws_obj': None, + 'ws_item': None + } + self.Proc_data['platform_bot_info_dict'] = None + + def run(self): + wsPath = f"ws://{self.Proc_data['bot_info_dict'].post_info.host}:{self.Proc_data['bot_info_dict'].post_info.port}/ws" + self.log(2, OlivOS.L10NAPI.getTrans('OlivOS OPQBot link server [{0}] is running on [{1}]', [self.Proc_name, wsPath], modelName)) + threading.Thread( + target=self.message_router, + args=() + ).start() + while True: + try: + self.Proc_data['extend_data']['websocket_url'] = wsPath + except: + self.Proc_data['extend_data']['websocket_url'] = None + if self.Proc_data['extend_data']['websocket_url'] is not None: + self.run_websocket_rx_connect_start() + time.sleep(self.Proc_info.scan_interval) + + def on_message(self, ws, message): + try: + #print(message) + rx_data = json.loads(message) + rx_obj = OlivOS.OPQBotSDK.PAYLOAD.rxPacket(data=rx_data) + if rx_obj.active: + sdk_event = OlivOS.OPQBotSDK.event(rx_obj, self.Proc_data['bot_info_dict']) + tx_packet_data = OlivOS.pluginAPI.shallow.rx_packet(sdk_event) + self.Proc_info.tx_queue.put(tx_packet_data, block=False) + except: + pass + + def on_error(self, ws, error): + self.log(0, OlivOS.L10NAPI.getTrans( + 'OlivOS OPQBot link server [{0}] websocket link error', + [self.Proc_name], + modelName + )) + + def on_close(self, ws, close_status_code, close_msg): + self.log(0, OlivOS.L10NAPI.getTrans( + 'OlivOS OPQBot link server [{0}] websocket link close', + [self.Proc_name], + modelName + )) + + def on_open(self, ws: websocket.WebSocketApp): + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS OPQBot link server [{0}] websocket link start', + [self.Proc_name], + modelName + )) + + def run_websocket_rx_connect_start(self): + websocket.enableTrace(False) + ws = websocket.WebSocketApp( + self.Proc_data['extend_data']['websocket_url'], + on_open=self.on_open, + on_message=self.on_message, + on_error=self.on_error, + on_close=self.on_close + ) + self.Proc_data['extend_data']['ws_obj'] = ws + self.Proc_data['extend_data']['ws_item'] = uuid.uuid4() + proxy_set = OlivOS.webTool.get_system_proxy_tuple('http') + ws.run_forever(http_proxy_host=proxy_set[0], http_proxy_port=proxy_set[1], proxy_type=proxy_set[2]) + self.Proc_data['extend_data']['ws_obj'] = None + self.Proc_data['extend_data']['ws_item'] = None + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS OPQBot link server [{0}] websocket link lost', + [self.Proc_name], + modelName + )) + + def message_router(self): + while True: + if self.Proc_data['extend_data']['ws_obj'] is None or self.Proc_info.rx_queue.empty(): + time.sleep(self.Proc_info.scan_interval) + else: + try: + rx_packet_data = self.Proc_info.rx_queue.get(block=False) + except: + rx_packet_data = None + if rx_packet_data is not None: + if 'data' in rx_packet_data.key and 'action' in rx_packet_data.key['data']: + if 'send' == rx_packet_data.key['data']['action']: + if 'data' in rx_packet_data.key['data']: + #print(rx_packet_data.key['data']['data']) + self.Proc_data['extend_data']['ws_obj'].send(rx_packet_data.key['data']['data']) diff --git a/OlivOS/adapter/OPQBot/OPQBotSDK.py b/OlivOS/adapter/OPQBot/OPQBotSDK.py new file mode 100644 index 00000000..bab19d3d --- /dev/null +++ b/OlivOS/adapter/OPQBot/OPQBotSDK.py @@ -0,0 +1,921 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/OPQBotSDK.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import time +import json +import uuid +import hashlib +import copy +import base64 +import requests as req +from urllib import parse +import traceback + +import OlivOS + +gBotIdDict = {} + +gResReg = {} + +gUinfoReg = {} + +gMsgSeqToGroupCodeReg = {} + +gFriendReqTsReg = {} + +class bot_info_T(object): + def __init__(self, id=-1): + self.id = id + self.debug_mode = False + self.debug_logger = None + + +def get_SDK_bot_info_from_Plugin_bot_info(plugin_bot_info): + res = bot_info_T(id=plugin_bot_info.id) + return res + + +def get_SDK_bot_info_from_Event(target_event): + res = get_SDK_bot_info_from_Plugin_bot_info(target_event.bot_info) + return res + + +class event(object): + def __init__(self, payload_data=None, bot_info=None): + self.payload = payload_data + self.platform = {'sdk': 'onebot', 'platform': 'qq', 'model': 'opqbot_default'} + if type(bot_info.platform) is dict: + self.platform.update(bot_info.platform) + self.active = False + if self.payload is not None: + self.active = True + self.base_info = {} + if self.active: + self.base_info['time'] = int(time.time()) + self.base_info['self_id'] = self.payload.CurrentQQ + self.base_info['post_type'] = None + +def get_message(Content:str, AtUinLists:list): + res_msg = Content + if type(AtUinLists) is list: + for AtUin in AtUinLists: + res_msg = res_msg.replace(f'@{AtUin["Nick"]}', f'[OP:at,id={AtUin["Uin"]}]') + return res_msg + +def get_Event_from_SDK(target_event): + global gFriendReqTsReg + target_event.base_info['time'] = target_event.sdk_event.base_info['time'] + target_event.base_info['self_id'] = str(target_event.sdk_event.base_info['self_id']) + target_event.base_info['type'] = target_event.sdk_event.base_info['post_type'] + target_event.platform['sdk'] = target_event.sdk_event.platform['sdk'] + target_event.platform['platform'] = target_event.sdk_event.platform['platform'] + target_event.platform['model'] = target_event.sdk_event.platform['model'] + target_event.plugin_info['message_mode_rx'] = 'olivos_string' + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + if target_event.sdk_event.payload.active: + if target_event.sdk_event.payload.EventName == 'CgiBaseResponse': + if type(target_event.sdk_event.payload.ReqId) is int \ + and type(target_event.sdk_event.payload.ResponseData) is dict \ + and type(target_event.sdk_event.payload.Ret) is int: + target_event.active = False + waitForResSet( + str(target_event.sdk_event.payload.ReqId), + target_event.sdk_event.payload.data + ) + elif target_event.sdk_event.payload.EventName == 'ON_EVENT_GROUP_NEW_MSG': + if type(target_event.sdk_event.payload.EventData) is dict \ + and 'MsgHead' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgHead']) is dict \ + and 'FromUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) is int \ + and 'ToUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['ToUin']) is int \ + and 'SenderUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['SenderUin']) is int \ + and 'SenderNick' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['SenderNick']) is str \ + and 'MsgBody' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgBody']) is dict \ + and 'Content' in target_event.sdk_event.payload.EventData['MsgBody'] \ + and type(target_event.sdk_event.payload.EventData['MsgBody']['Content']) is str: + target_event.active = True + message_obj = OlivOS.messageAPI.Message_templet( + 'olivos_string', + get_message( + Content = target_event.sdk_event.payload.EventData['MsgBody']['Content'], + AtUinLists = target_event.sdk_event.payload.EventData['MsgBody'].get('AtUinLists', []) + ) + ) + if str(target_event.sdk_event.payload.EventData['MsgHead']['SenderUin']) == str(target_event.base_info['self_id']): + target_event.plugin_info['func_type'] = 'group_message_sent' + target_event.data = target_event.group_message_sent( + str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']), + str(target_event.sdk_event.payload.EventData['MsgHead']['SenderUin']), + message_obj, + 'group' + ) + else: + target_event.plugin_info['func_type'] = 'group_message' + target_event.data = target_event.group_message( + str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']), + str(target_event.sdk_event.payload.EventData['MsgHead']['SenderUin']), + message_obj, + 'group' + ) + target_event.data.message_sdk = message_obj + target_event.data.message_id = str(-1) + target_event.data.raw_message = message_obj + target_event.data.raw_message_sdk = message_obj + target_event.data.font = None + target_event.data.sender['user_id'] = str(target_event.sdk_event.payload.EventData['MsgHead']['SenderUin']) + target_event.data.sender['nickname'] = target_event.sdk_event.payload.EventData['MsgHead']['SenderNick'] + target_event.data.sender['id'] = target_event.data.sender['user_id'] + target_event.data.sender['name'] = target_event.data.sender['nickname'] + target_event.data.sender['sex'] = 'unknown' + target_event.data.sender['age'] = 0 + if 'role' in target_event.data.sender: + target_event.data.sender.pop('role') + target_event.data.host_id = None + elif target_event.sdk_event.payload.EventName == 'ON_EVENT_FRIEND_NEW_MSG': + if type(target_event.sdk_event.payload.EventData) is dict \ + and 'MsgHead' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgHead']) is dict \ + and 'MsgType' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and target_event.sdk_event.payload.EventData['MsgHead']['MsgType'] == 528 \ + and 'C2cCmd' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and target_event.sdk_event.payload.EventData['MsgHead']['C2cCmd'] == 35 \ + and 'ToUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['ToUin']) is int \ + and 'FromUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) is int \ + and 'FromUid' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['FromUid']) is str: + tmp_ToUin = str(target_event.sdk_event.payload.EventData['MsgHead']['ToUin']) + tmp_FromUin = str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) + if tmp_ToUin != tmp_FromUin: + if int(time.time()) - gFriendReqTsReg.get(tmp_FromUin, -1) >= 5: + gFriendReqTsReg[tmp_FromUin] = int(time.time()) + target_event.active = True + target_event.plugin_info['func_type'] = 'friend_add_request' + target_event.data = target_event.friend_add_request( + tmp_FromUin, + '' + ) + target_event.data.flag = str(target_event.sdk_event.payload.EventData['MsgHead']['FromUid']) + elif type(target_event.sdk_event.payload.EventData) is dict \ + and 'MsgHead' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgHead']) is dict \ + and 'FromUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) is int \ + and 'ToUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['ToUin']) is int \ + and 'MsgBody' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgBody']) is dict \ + and 'Content' in target_event.sdk_event.payload.EventData['MsgBody'] \ + and type(target_event.sdk_event.payload.EventData['MsgBody']['Content']) is str: + target_event.active = True + message_obj = OlivOS.messageAPI.Message_templet( + 'olivos_string', + get_message( + Content = target_event.sdk_event.payload.EventData['MsgBody']['Content'], + AtUinLists = target_event.sdk_event.payload.EventData['MsgBody'].get('AtUinLists', []) + ) + ) + target_event.plugin_info['func_type'] = 'private_message' + target_event.data = target_event.private_message( + str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']), + message_obj, + 'private' + ) + target_event.data.message_sdk = message_obj + target_event.data.message_id = str(-1) + target_event.data.raw_message = message_obj + target_event.data.raw_message_sdk = message_obj + target_event.data.font = None + target_event.data.sender['user_id'] = str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) + target_event.data.sender['nickname'] = '用户' + target_event.data.sender['id'] = target_event.data.sender['user_id'] + target_event.data.sender['name'] = target_event.data.sender['nickname'] + target_event.data.sender['sex'] = 'unknown' + target_event.data.sender['age'] = 0 + target_event.data.host_id = None + elif target_event.sdk_event.payload.EventName == 'ON_EVENT_GROUP_INVITE': + if type(target_event.sdk_event.payload.EventData) is dict \ + and 'MsgHead' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgHead']) is dict \ + and 'FromUin' in target_event.sdk_event.payload.EventData['MsgHead'] \ + and type(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']) is int \ + and 'Event' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['Event']) is dict \ + and 'Invitee' in target_event.sdk_event.payload.EventData['Event'] \ + and type(target_event.sdk_event.payload.EventData['Event']['Invitee']) in [str, int] \ + and 'Invitor' in target_event.sdk_event.payload.EventData['Event'] \ + and type(target_event.sdk_event.payload.EventData['Event']['Invitor']) in [str, int]: + target_event.active = True + target_event.plugin_info['func_type'] = 'group_member_increase' + target_event.data = target_event.group_member_increase( + str(target_event.sdk_event.payload.EventData['MsgHead']['FromUin']), + str(target_event.sdk_event.payload.EventData['Event']['Invitor']), + str(target_event.sdk_event.payload.EventData['Event']['Invitee']) + ) + target_event.data.action = 'approve' + elif False and target_event.sdk_event.payload.EventName == 'ON_EVENT_FRIEND_SYSTEM_MSG_NOTIFY': + if type(target_event.sdk_event.payload.EventData) is dict \ + and 'ReqUid' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['ReqUid']) is str \ + and 'Status' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['Status']) is int \ + and 'MsgAdditional' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgAdditional']) is str: + target_event.active = True + target_event.plugin_info['func_type'] = 'friend_add_request' + uin = None + if False and OlivOS.pluginAPI.gProc is not None: + uin = event_action.getUinfo( + target_event = target_event, + Uid = target_event.sdk_event.payload.EventData['ReqUid'], + control_queue = OlivOS.pluginAPI.gProc.Proc_info.control_queue + ) + if uin is None: + uin = -1 + target_event.data = target_event.friend_add_request( + str(uin), + target_event.sdk_event.payload.EventData['MsgAdditional'] + ) + target_event.data.flag = str(target_event.sdk_event.payload.EventData['ReqUid']) + elif target_event.sdk_event.payload.EventName == 'ON_EVENT_GROUP_SYSTEM_MSG_NOTIFY': + if type(target_event.sdk_event.payload.EventData) is dict \ + and 'MsgType' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgType']) is int \ + and 'GroupCode' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['GroupCode']) is int \ + and 'MsgSeq' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgSeq']) is int \ + and 'Status' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['Status']) is int \ + and 'MsgAdditional' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['MsgAdditional']) is str \ + and 'ActorUid' in target_event.sdk_event.payload.EventData \ + and type(target_event.sdk_event.payload.EventData['ActorUid']) in [str, int]: + if target_event.sdk_event.payload.EventData['MsgType'] == 2 \ + and target_event.sdk_event.payload.EventData['Status'] == 1: + target_event.active = True + target_event.plugin_info['func_type'] = 'group_invite_request' + uin = None + if False and OlivOS.pluginAPI.gProc is not None: + uin = event_action.getUinfo( + target_event = target_event, + Uid = target_event.sdk_event.payload.EventData['ActorUid'], + control_queue = OlivOS.pluginAPI.gProc.Proc_info.control_queue + ) + if uin is None: + uin = -1 + target_event.data = target_event.group_invite_request( + str(target_event.sdk_event.payload.EventData['GroupCode']), + str(uin), + target_event.sdk_event.payload.EventData['MsgAdditional'] + ) + target_event.data.flag = str(target_event.sdk_event.payload.EventData['MsgSeq']) + gMsgSeqToGroupCodeReg[target_event.data.flag] = target_event.sdk_event.payload.EventData['GroupCode'] + + +''' +对于WEBSOCKET接口的PAYLOAD实现 +''' + + +class payload_template(object): + def __init__(self, data=None, is_rx=False): + self.active = True + self.data = None + self.EventName = None + self.EventData = None + self.CurrentQQ = None + self.ReqId = int(getHash(str(uuid.uuid4())), 16) % 1000000000000 + self.CgiCmd = None + self.ResponseData = None + self.CgiBaseResponse = None + self.Ret = None + self.load(data, is_rx) + + def dump(self): + res = json.dumps(obj=self.data) + return res + + def dump_CurrentPacket(self): + res = json.dumps( + obj = { + 'CurrentPacket': { + 'EventData': self.EventData, + 'EventName': self.EventName + }, + 'CurrentQQ': int(self.CurrentQQ) + } + ) + return res + + def load(self, data, is_rx: bool): + self.active = False + if is_rx \ + and type(data) is dict \ + and 'CurrentQQ' in data \ + and type(data['CurrentQQ']) in [int, str] \ + and 'CurrentPacket' in data \ + and type(data['CurrentPacket']) is dict \ + and 'EventName' in data['CurrentPacket'] \ + and type(data['CurrentPacket']['EventName']) is str \ + and 'EventData' in data['CurrentPacket'] \ + and type(data['CurrentPacket']['EventData']) is dict: + self.active = True + self.data = data + self.EventName = data['CurrentPacket']['EventName'] + self.EventData = data['CurrentPacket']['EventData'] + self.CurrentQQ = data['CurrentQQ'] + elif is_rx \ + and type(data) is dict \ + and 'CgiBaseResponse' in data \ + and type(data['CgiBaseResponse']) is dict \ + and 'Ret' in data['CgiBaseResponse'] \ + and type(data['CgiBaseResponse']['Ret']) is int \ + and 'ReqId' in data \ + and type(data['ReqId']) is int \ + and 'ResponseData' in data \ + and type(data['ResponseData']) is dict: + self.active = True + self.data = data + self.ReqId = data['ReqId'] + self.ResponseData = data['ResponseData'] + self.CgiBaseResponse = data['CgiBaseResponse'] + self.Ret = data['CgiBaseResponse']['Ret'] + self.EventName = 'CgiBaseResponse' + return self + + +def getHash(key): + hash_tmp = hashlib.new('md5') + hash_tmp.update(str(key).encode(encoding='UTF-8')) + return hash_tmp.hexdigest() + +def getIdBackport(id): + res = id + try: + res = int(id) + except: + res = id + return res + +class PAYLOAD(object): + class rxPacket(payload_template): + def __init__(self, data): + payload_template.__init__(self, data, True) + + class MessageSvc_PbSendMsg(payload_template): + def __init__(self, ToUin:'int|str', ToType:int, Content:str, CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "MessageSvc.PbSendMsg" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + "ToUin": getIdBackport(ToUin), + "ToType": ToType, + "Content": Content + } + } + + class MessageSvc_PbSendMsg_all(payload_template): + def __init__(self, ToUin:'int|str', ToType:int, dataPatch:dict, CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "MessageSvc.PbSendMsg" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + "ToUin": getIdBackport(ToUin), + "ToType": ToType + } + } + self.data['CgiRequest'].update(dataPatch) + + class exitGroup(payload_template): + def __init__(self, Uin:'int|str', CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "SsoGroup.Op" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + "OpCode": 4247, + "Uin": getIdBackport(Uin) + } + } + + class GetGroupLists(payload_template): + def __init__(self, CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "GetGroupLists" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": {} + } + + class PicUp_DataUp(payload_template): + def __init__( + self, + CommandId:int, + CurrentQQ:'int|str', + FilePath:'str|None' = None, + FileUrl:'str|None' = None, + Base64Buf:'str|None' = None, + ): + payload_template.__init__(self) + self.CgiCmd = "PicUp.DataUp" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": {} + } + self.data['CgiRequest']['CommandId'] = CommandId + if FilePath is not None: + self.data['CgiRequest']['FilePath'] = FilePath + if FileUrl is not None: + self.data['CgiRequest']['FileUrl'] = FileUrl + if Base64Buf is not None: + self.data['CgiRequest']['Base64Buf'] = Base64Buf + + class QueryUinByUid(payload_template): + def __init__(self, UidQuery:'list[str]', CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "QueryUinByUid" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + 'Uids': UidQuery + } + } + + class SystemMsgAction_Friend(payload_template): + def __init__(self, ReqUid:int, OpCode:int, CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "SystemMsgAction.Friend" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + "ReqUid": ReqUid, + "OpCode": OpCode + } + } + + class SystemMsgAction_Group(payload_template): + def __init__(self, MsgSeq:int, MsgType:int, GroupCode:int, OpCode:int, CurrentQQ:'int|str'): + payload_template.__init__(self) + self.CgiCmd = "SystemMsgAction.Group" + self.CurrentQQ = str(CurrentQQ) + self.data = { + "ReqId": self.ReqId, + "BotUin": str(self.CurrentQQ), + "CgiCmd": self.CgiCmd, + "CgiRequest": { + "MsgSeq": MsgSeq, + "MsgType": MsgType, + "GroupCode": GroupCode, + "OpCode": OpCode + } + } + + +# 支持OlivOS API调用的方法实现 +class event_action(object): + def getUinfo(target_event, Uid, control_queue): + global gUinfoReg + res = Uid + if Uid in gUinfoReg: + res = gUinfoReg[Uid] + else: + res_list = event_action.getUinfoCache( + target_event = target_event, + UidQuery = [Uid], + control_queue = control_queue + ) + if len(res_list) == 1: + res = res_list[0] + return res + + def getUinfoCache(target_event, UidQuery, control_queue): + global gUinfoReg + res_tmp = {} + res = [] + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + this_msg = PAYLOAD.QueryUinByUid( + UidQuery = UidQuery, + CurrentQQ = target_event.base_info['self_id'] + ) + waitForResReady(str(this_msg.ReqId)) + send_ws_event( + plugin_event_bot_hash, + this_msg.dump(), + control_queue + ) + res_raw = waitForRes(str(this_msg.ReqId)) + raw_obj = init_api_json(res_raw) + if raw_obj is not None: + if type(raw_obj) is list: + for raw_obj_this in raw_obj: + res_tmp[str(raw_obj_this['Uid'])] = raw_obj_this['Uin'] + gUinfoReg.update(res_tmp) + for UidQuery_this in UidQuery: + res.append(res_tmp.get(UidQuery_this, None)) + return res + + def send_solo_msg(target_event, target_type, target_id, message, control_queue): + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + if len(message) > 0: + send_ws_event( + plugin_event_bot_hash, + PAYLOAD.MessageSvc_PbSendMsg( + ToUin = target_id, + ToType = 2 if 'group' == target_type else 1, + Content = message, + CurrentQQ = target_event.base_info['self_id'] + ).dump(), + control_queue + ) + + def send_solo_all_msg(target_event, target_type, target_id, dataPatch, control_queue): + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + if type(dataPatch) is dict: + send_ws_event( + plugin_event_bot_hash, + PAYLOAD.MessageSvc_PbSendMsg_all( + ToUin = target_id, + ToType = 2 if 'group' == target_type else 1, + dataPatch = dataPatch, + CurrentQQ = target_event.base_info['self_id'] + ).dump(), + control_queue + ) + + + def send_msg(target_event, target_type, target_id, message, control_queue): + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + message_new = '' + message_obj = OlivOS.messageAPI.Message_templet( + 'olivos_string', + message + ) + count_data = 0 + size_data = len(message_obj.data) + flag_now_type = 'string' + flag_now_type_last = flag_now_type + if message_obj.active: + for data_this in message_obj.data: + res = None + count_data += 1 + flag_now_type_last = flag_now_type + if type(data_this) is OlivOS.messageAPI.PARA.text: + message_new += data_this.data['text'] + flag_now_type = 'string' + elif type(data_this) is OlivOS.messageAPI.PARA.image: + res = event_action.setResourceUploadFast( + target_event = target_event, + control_queue = control_queue, + url = data_this.data['file'], + type_path = 'images', + type_chat = 2 if 'group' == target_type else 1 + ) + flag_now_type = 'image' + if size_data == count_data\ + or (flag_now_type_last != flag_now_type \ + and flag_now_type_last == 'string' \ + and len(message_new) > 0): + event_action.send_solo_msg( + target_event = target_event, + target_type = target_type, + target_id = target_id, + message = message_new, + control_queue = control_queue + ) + message_new = '' + time.sleep(1) + if flag_now_type == 'image': + if res is not None: + event_action.send_solo_all_msg( + target_event = target_event, + target_type = target_type, + target_id = target_id, + dataPatch = { + 'Images': [ + { + "FileId": res[2], + "FileMd5": res[0], + "FileSize": res[1], + "Height": 1920, + "Width": 1080 + } + ] + }, + control_queue = control_queue + ) + time.sleep(1) + + # 现场上传的就地实现 + def setResourceUploadFast(target_event, control_queue, url: str, type_path: str = 'images', type_chat: int = 2): + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + res = [None, None, None] + data_obj = None + try: + pic_file = None + if url.startswith("base64://"): + data_obj = PAYLOAD.PicUp_DataUp( + CommandId = type_chat, + Base64Buf = url, + CurrentQQ = target_event.base_info['self_id'] + ) + else: + url_parsed = parse.urlparse(url) + if url_parsed.scheme in ["http", "https"]: + data_obj = PAYLOAD.PicUp_DataUp( + CommandId = type_chat, + FileUrl = url, + CurrentQQ = target_event.base_info['self_id'] + ) + else: + file_path = url_parsed.path + file_path = OlivOS.contentAPI.resourcePathTransform(type_path, file_path) + data_obj = PAYLOAD.PicUp_DataUp( + CommandId = type_chat, + FilePath = file_path, + CurrentQQ = target_event.base_info['self_id'] + ) + + if data_obj is not None: + waitForResReady(str(data_obj.ReqId)) + send_ws_event( + plugin_event_bot_hash, + data_obj.dump(), + control_queue + ) + res_raw = waitForRes(str(data_obj.ReqId)) + raw_obj = init_api_json(res_raw) + if raw_obj is not None: + if type(raw_obj) is dict \ + and 'FileMd5' in raw_obj \ + and type(raw_obj['FileMd5']) is str \ + and 'FileSize' in raw_obj \ + and type(raw_obj['FileSize']) is int \ + and 'FileId' in raw_obj \ + and type(raw_obj['FileId']) is int: + res = [raw_obj['FileMd5'], raw_obj['FileSize'], raw_obj['FileId']] + except Exception as e: + traceback.print_exc() + res = [None, None, None] + return res + + def set_group_leave(target_event, group_id, control_queue): + if target_event.bot_info != None: + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + send_ws_event( + plugin_event_bot_hash, + PAYLOAD.exitGroup( + Uin = group_id, + CurrentQQ = target_event.base_info['self_id'] + ).dump(), + control_queue + ) + + def set_friend_add_request(target_event, flag:str, approve:bool, control_queue): + if target_event.bot_info != None: + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + OpCode_int = 3 if approve is True else 5 + send_ws_event( + plugin_event_bot_hash, + PAYLOAD.SystemMsgAction_Friend( + ReqUid = flag, + OpCode = OpCode_int, + CurrentQQ = target_event.base_info['self_id'] + ).dump(), + control_queue + ) + + def set_group_add_request(target_event, flag:str, sub_type:str, approve:bool, control_queue): + if target_event.bot_info != None: + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + sub_type_int = None + OpCode_int = None + GroupCode_this = None + if sub_type == 'invite': + sub_type_int = 2 + GroupCode_this = gMsgSeqToGroupCodeReg.get(str(flag), None) + if approve is True: + OpCode_int = 1 + else: + OpCode_int = 2 + elif sub_type == 'add': + pass + if sub_type_int is not None: + send_ws_event( + plugin_event_bot_hash, + PAYLOAD.SystemMsgAction_Group( + MsgSeq = int(flag), + MsgType = sub_type_int, + GroupCode = GroupCode_this, + OpCode = OpCode_int, + CurrentQQ = target_event.base_info['self_id'] + ).dump(), + control_queue + ) + + + def get_group_list(target_event:OlivOS.API.Event, control_queue): + res_data = OlivOS.contentAPI.api_result_data_template.get_group_list() + if target_event.bot_info != None: + plugin_event_bot_hash = OlivOS.API.getBotHash( + bot_id=target_event.base_info['self_id'], + platform_sdk=target_event.platform['sdk'], + platform_platform=target_event.platform['platform'], + platform_model=target_event.platform['model'] + ) + this_msg = PAYLOAD.GetGroupLists( + CurrentQQ = target_event.base_info['self_id'] + ) + waitForResReady(str(this_msg.ReqId)) + send_ws_event( + plugin_event_bot_hash, + this_msg.dump(), + control_queue + ) + res_raw = waitForRes(str(this_msg.ReqId)) + raw_obj = init_api_json(res_raw) + if raw_obj is not None: + if type(raw_obj) is dict \ + and 'GroupLists' in raw_obj \ + and type(raw_obj['GroupLists']) is list: + res_data['active'] = True + for raw_obj_this in raw_obj['GroupLists']: + tmp_res_data_this = OlivOS.contentAPI.api_result_data_template.get_user_info_strip() + tmp_res_data_this['name'] = init_api_do_mapping_for_dict(raw_obj_this, ['GroupName'], str) + tmp_res_data_this['id'] = init_api_do_mapping_for_dict(raw_obj_this, ['GroupCode'], int) + tmp_res_data_this['memo'] = '' + tmp_res_data_this['member_count'] = init_api_do_mapping_for_dict(raw_obj_this, ['MemberCnt'], int) + tmp_res_data_this['max_member_count'] = init_api_do_mapping_for_dict(raw_obj_this, ['GroupCnt'], int) + res_data['data'].append(tmp_res_data_this) + return res_data + + +def sendControlEventSend(action, data, control_queue): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet( + action, + data + ), + block=False + ) + + +def send_ws_event(hash, data, control_queue): + sendControlEventSend('send', { + 'target': { + 'type': 'OPQBot_link', + 'hash': hash + }, + 'data': { + 'action': 'send', + 'data': data + } + }, + control_queue + ) + +def init_api_json(raw:dict): + res_data = None + if type(raw) is dict \ + and 'CgiBaseResponse' in raw \ + and type(raw['CgiBaseResponse']) is dict \ + and 'Ret' in raw['CgiBaseResponse'] \ + and type(raw['CgiBaseResponse']['Ret']) is int \ + and raw['CgiBaseResponse']['Ret'] == 0 \ + and 'ReqId' in raw \ + and type(raw['ReqId']) is int \ + and 'ResponseData' in raw \ + and type(raw['ResponseData']) in [dict, list]: + res_data = copy.deepcopy(raw['ResponseData']) + return res_data + +def init_api_do_mapping(src_type, src_data): + if type(src_data) == src_type: + return src_data + +def init_api_do_mapping_for_dict(src_data, path_list, src_type): + res_data = None + flag_active = True + tmp_src_data = src_data + for path_list_this in path_list: + if type(tmp_src_data) == dict: + if path_list_this in tmp_src_data: + tmp_src_data = tmp_src_data[path_list_this] + else: + return None + else: + return None + res_data = init_api_do_mapping(src_type, tmp_src_data) + return res_data + +def waitForResSet(echo:str, data): + global gResReg + if echo in gResReg: + gResReg[echo] = data + +def waitForResReady(echo:str): + global gResReg + gResReg[echo] = None + +def waitForRes(echo:str): + global gResReg + res = None + interval = 0.1 + limit = 30 + index_limit = int(limit / interval) + for i in range(index_limit): + time.sleep(interval) + if echo in gResReg \ + and gResReg[echo] is not None: + res = gResReg[echo] + gResReg.pop(echo) + break + return res diff --git a/OlivOS/biliLiveLinkServerAPI.py b/OlivOS/adapter/biliLive/biliLiveLinkServerAPI.py similarity index 100% rename from OlivOS/biliLiveLinkServerAPI.py rename to OlivOS/adapter/biliLive/biliLiveLinkServerAPI.py diff --git a/OlivOS/biliLiveSDK.py b/OlivOS/adapter/biliLive/biliLiveSDK.py similarity index 100% rename from OlivOS/biliLiveSDK.py rename to OlivOS/adapter/biliLive/biliLiveSDK.py diff --git a/OlivOS/dingtalkLinkServerAPI.py b/OlivOS/adapter/dingtalk/dingtalkLinkServerAPI.py similarity index 100% rename from OlivOS/dingtalkLinkServerAPI.py rename to OlivOS/adapter/dingtalk/dingtalkLinkServerAPI.py diff --git a/OlivOS/dingtalkSDK.py b/OlivOS/adapter/dingtalk/dingtalkSDK.py similarity index 100% rename from OlivOS/dingtalkSDK.py rename to OlivOS/adapter/dingtalk/dingtalkSDK.py diff --git a/OlivOS/discordLinkServerAPI.py b/OlivOS/adapter/discord/discordLinkServerAPI.py similarity index 100% rename from OlivOS/discordLinkServerAPI.py rename to OlivOS/adapter/discord/discordLinkServerAPI.py diff --git a/OlivOS/discordSDK.py b/OlivOS/adapter/discord/discordSDK.py similarity index 96% rename from OlivOS/discordSDK.py rename to OlivOS/adapter/discord/discordSDK.py index e6834a29..cc7b9103 100644 --- a/OlivOS/discordSDK.py +++ b/OlivOS/adapter/discord/discordSDK.py @@ -78,8 +78,9 @@ class intents_T(IntEnum): class bot_info_T(object): - def __init__(self, id=-1, access_token=None, model='private'): + def __init__(self, id=-1, port=-1, access_token=None, model='private'): self.id = id + self.intents = port self.access_token = access_token self.model = model self.debug_mode = False @@ -89,6 +90,7 @@ def __init__(self, id=-1, access_token=None, model='private'): def get_SDK_bot_info_from_Plugin_bot_info(plugin_bot_info): res = bot_info_T( plugin_bot_info.id, + plugin_bot_info.post_info.port, plugin_bot_info.post_info.access_token ) res.debug_mode = plugin_bot_info.debug_mode @@ -178,9 +180,14 @@ def __init__(self, data): payload_template.__init__(self, data, True) class sendIdentify(payload_template): - def __init__(self, bot_info, - intents=(int(intents_T.GUILDS) | int(intents_T.DIRECT_MESSAGE | intents_T.GUILD_MESSAGES))): + def __init__( + self, + bot_info:bot_info_T, + intents=(int(intents_T.GUILDS) | int(intents_T.DIRECT_MESSAGE | intents_T.GUILD_MESSAGES)) + ): tmp_intents = intents + if bot_info.model == 'intents': + tmp_intents = bot_info.intents payload_template.__init__(self) self.data.op = 2 try: @@ -246,14 +253,20 @@ def do_api(self, req_type='POST', proxy=None): msg_res = None if req_type == 'POST': - msg_res = req.request("POST", send_url, headers=headers, data=payload, - proxies=OlivOS.webTool.get_system_proxy()) + msg_res = req.request( + "POST", + send_url, + headers=headers, + data=payload, + proxies=OlivOS.webTool.get_system_proxy() + ) elif req_type == 'GET': - msg_res = req.request("GET", send_url, headers=headers, proxies=OlivOS.webTool.get_system_proxy()) - - if self.bot_info.debug_mode: - if self.bot_info.debug_logger is not None: - self.bot_info.debug_logger.log(0, self.node_ext + ' - sendding succeed: ' + msg_res.text) + msg_res = req.request( + "GET", + send_url, + headers=headers, + proxies=OlivOS.webTool.get_system_proxy() + ) self.res = msg_res.text return msg_res.text diff --git a/OlivOS/dodoLinkSDK.py b/OlivOS/adapter/dodo/dodoLinkSDK.py similarity index 100% rename from OlivOS/dodoLinkSDK.py rename to OlivOS/adapter/dodo/dodoLinkSDK.py diff --git a/OlivOS/dodoLinkServerAPI.py b/OlivOS/adapter/dodo/dodoLinkServerAPI.py similarity index 100% rename from OlivOS/dodoLinkServerAPI.py rename to OlivOS/adapter/dodo/dodoLinkServerAPI.py diff --git a/OlivOS/dodoPollServerAPI.py b/OlivOS/adapter/dodo/dodoPollServerAPI.py similarity index 100% rename from OlivOS/dodoPollServerAPI.py rename to OlivOS/adapter/dodo/dodoPollServerAPI.py diff --git a/OlivOS/dodoSDK.py b/OlivOS/adapter/dodo/dodoSDK.py similarity index 100% rename from OlivOS/dodoSDK.py rename to OlivOS/adapter/dodo/dodoSDK.py diff --git a/OlivOS/dodobotEASDK.py b/OlivOS/adapter/dodo/dodobotEASDK.py similarity index 100% rename from OlivOS/dodobotEASDK.py rename to OlivOS/adapter/dodo/dodobotEASDK.py diff --git a/OlivOS/dodobotEAServerAPI.py b/OlivOS/adapter/dodo/dodobotEAServerAPI.py similarity index 100% rename from OlivOS/dodobotEAServerAPI.py rename to OlivOS/adapter/dodo/dodobotEAServerAPI.py diff --git a/OlivOS/dodobotEATXAPI.py b/OlivOS/adapter/dodo/dodobotEATXAPI.py similarity index 100% rename from OlivOS/dodobotEATXAPI.py rename to OlivOS/adapter/dodo/dodobotEATXAPI.py diff --git a/OlivOS/fanbookPollServerAPI.py b/OlivOS/adapter/fanbook/fanbookPollServerAPI.py similarity index 100% rename from OlivOS/fanbookPollServerAPI.py rename to OlivOS/adapter/fanbook/fanbookPollServerAPI.py diff --git a/OlivOS/fanbookSDK.py b/OlivOS/adapter/fanbook/fanbookSDK.py similarity index 100% rename from OlivOS/fanbookSDK.py rename to OlivOS/adapter/fanbook/fanbookSDK.py diff --git a/OlivOS/hackChatLinkServerAPI.py b/OlivOS/adapter/hackChat/hackChatLinkServerAPI.py similarity index 98% rename from OlivOS/hackChatLinkServerAPI.py rename to OlivOS/adapter/hackChat/hackChatLinkServerAPI.py index 945542a6..a75d1352 100644 --- a/OlivOS/hackChatLinkServerAPI.py +++ b/OlivOS/adapter/hackChat/hackChatLinkServerAPI.py @@ -21,8 +21,6 @@ import threading import OlivOS -from OlivOS import hackChatSDK -from OlivOS.qqGuildSDK import PAYLOAD modelName = 'hackChatLinkServerAPI' diff --git a/OlivOS/hackChatSDK.py b/OlivOS/adapter/hackChat/hackChatSDK.py similarity index 100% rename from OlivOS/hackChatSDK.py rename to OlivOS/adapter/hackChat/hackChatSDK.py diff --git a/OlivOS/kaiheilaLinkServerAPI.py b/OlivOS/adapter/kaiheila/kaiheilaLinkServerAPI.py similarity index 98% rename from OlivOS/kaiheilaLinkServerAPI.py rename to OlivOS/adapter/kaiheila/kaiheilaLinkServerAPI.py index 6bd01c78..36292c59 100644 --- a/OlivOS/kaiheilaLinkServerAPI.py +++ b/OlivOS/adapter/kaiheila/kaiheilaLinkServerAPI.py @@ -76,6 +76,7 @@ def on_message(self, ws, message): tmp_data_rx_obj = OlivOS.kaiheilaSDK.PAYLOAD.rxPacket( data=json.loads(message) ) + # print(json.dumps(json.loads(message), indent=4, ensure_ascii=False)) if tmp_data_rx_obj.data.s == 0: self.Proc_data['extend_data']['last_s'] = tmp_data_rx_obj.data.sn sdk_event = OlivOS.kaiheilaSDK.event(tmp_data_rx_obj, self.Proc_data['bot_info_dict']) diff --git a/OlivOS/kaiheilaSDK.py b/OlivOS/adapter/kaiheila/kaiheilaSDK.py similarity index 82% rename from OlivOS/kaiheilaSDK.py rename to OlivOS/adapter/kaiheila/kaiheilaSDK.py index 73ecaf1e..835097be 100644 --- a/OlivOS/kaiheilaSDK.py +++ b/OlivOS/adapter/kaiheila/kaiheilaSDK.py @@ -567,7 +567,106 @@ def get_Event_from_SDK(target_event): except Exception as e: traceback.print_exc() target_event.active = False - + else: + if 'type' in target_event.sdk_event.payload.data.d \ + and target_event.sdk_event.payload.data.d['type'] == 255 \ + and 'extra' in target_event.sdk_event.payload.data.d \ + and type(target_event.sdk_event.payload.data.d['extra']) is dict: + if 'body' in target_event.sdk_event.payload.data.d['extra'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']) is dict \ + and 'type' in target_event.sdk_event.payload.data.d['extra'] \ + and target_event.sdk_event.payload.data.d['extra']['type'] == 'message_btn_click': + if 'channel_type' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and target_event.sdk_event.payload.data.d['extra']['body']['channel_type'] == 'PERSON' \ + and 'target_id' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['target_id']) is str \ + and 'user_id' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_id']) is str \ + and 'value' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['value']) is str \ + and 'user_info' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']) is dict \ + and 'id' in target_event.sdk_event.payload.data.d['extra']['body']['user_info'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']['id']) is str \ + and 'username' in target_event.sdk_event.payload.data.d['extra']['body']['user_info'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']['username']) is str: + target_event.active = True + target_event.plugin_info['func_type'] = 'private_message' + msg = target_event.sdk_event.payload.data.d['extra']['body']['value'] + user_id = target_event.sdk_event.payload.data.d['extra']['body']['user_info']['id'] + user_name = target_event.sdk_event.payload.data.d['extra']['body']['user_info']['username'] + message_obj = OlivOS.messageAPI.Message_templet( + 'olivos_para', + [OlivOS.messageAPI.PARA.text(text=msg)] + ) + target_event.data = target_event.private_message( + user_id, + message_obj, + 'friend' + ) + target_event.data.message_sdk = message_obj + target_event.data.message_id = str(-1) + target_event.data.raw_message = message_obj + target_event.data.raw_message_sdk = message_obj + target_event.data.font = None + target_event.data.sender['user_id'] = user_id + target_event.data.sender['nickname'] = user_name + target_event.data.sender['id'] = user_id + target_event.data.sender['name'] = user_name + target_event.data.sender['sex'] = 'unknown' + target_event.data.sender['age'] = 0 + target_event.data.extend['flag_from_direct'] = True + if plugin_event_bot_hash in sdkSubSelfInfo: + target_event.data.extend['sub_self_id'] = str(sdkSubSelfInfo[plugin_event_bot_hash]) + elif 'channel_type' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and target_event.sdk_event.payload.data.d['extra']['body']['channel_type'] == 'GROUP' \ + and 'target_id' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['target_id']) is str \ + and 'guild_id' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['guild_id']) is str \ + and 'user_id' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_id']) is str \ + and 'value' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['value']) is str \ + and 'user_info' in target_event.sdk_event.payload.data.d['extra']['body'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']) is dict \ + and 'id' in target_event.sdk_event.payload.data.d['extra']['body']['user_info'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']['id']) is str \ + and 'username' in target_event.sdk_event.payload.data.d['extra']['body']['user_info'] \ + and type(target_event.sdk_event.payload.data.d['extra']['body']['user_info']['username']) is str: + target_event.active = True + target_event.plugin_info['func_type'] = 'group_message' + msg = target_event.sdk_event.payload.data.d['extra']['body']['value'] + host_id = target_event.sdk_event.payload.data.d['extra']['body']['guild_id'] + group_id = target_event.sdk_event.payload.data.d['extra']['body']['target_id'] + user_id = target_event.sdk_event.payload.data.d['extra']['body']['user_info']['id'] + user_name = target_event.sdk_event.payload.data.d['extra']['body']['user_info']['username'] + message_obj = OlivOS.messageAPI.Message_templet( + 'olivos_para', + [OlivOS.messageAPI.PARA.text(text=msg)] + ) + target_event.data = target_event.group_message( + group_id, + user_id, + message_obj, + 'group' + ) + target_event.data.message_sdk = message_obj + target_event.data.message_id = str(-1) + target_event.data.raw_message = message_obj + target_event.data.raw_message_sdk = message_obj + target_event.data.font = None + target_event.data.sender['user_id'] = user_id + target_event.data.sender['nickname'] = user_name + target_event.data.sender['id'] = user_id + target_event.data.sender['name'] = user_name + target_event.data.sender['sex'] = 'unknown' + target_event.data.sender['age'] = 0 + target_event.data.sender['role'] = 'member' + target_event.data.host_id = host_id + target_event.data.extend['flag_from_direct'] = False + if plugin_event_bot_hash in sdkSubSelfInfo: + target_event.data.extend['sub_self_id'] = str(sdkSubSelfInfo[plugin_event_bot_hash]) # 支持OlivOS API调用的方法实现 class event_action(object): diff --git a/OlivOS/mhyVilaLinkServerAPI.py b/OlivOS/adapter/mhyVila/mhyVilaLinkServerAPI.py similarity index 100% rename from OlivOS/mhyVilaLinkServerAPI.py rename to OlivOS/adapter/mhyVila/mhyVilaLinkServerAPI.py diff --git a/OlivOS/mhyVilaSDK.py b/OlivOS/adapter/mhyVila/mhyVilaSDK.py similarity index 89% rename from OlivOS/mhyVilaSDK.py rename to OlivOS/adapter/mhyVila/mhyVilaSDK.py index 57becc9a..a377489e 100644 --- a/OlivOS/mhyVilaSDK.py +++ b/OlivOS/adapter/mhyVila/mhyVilaSDK.py @@ -497,3 +497,66 @@ def send_group_msg(target_event, chat_id, message, host_id = None): api_obj.data.object_name = 'MHY:Text' api_obj.do_api('POST') return None + + def create_panel_message(target_event, chat_id, object_name, content:dict, host_id = None): + res_data = OlivOS.contentAPI.api_result_data_template.universal_result() + res_data['active'] = True + if host_id is None: + try: + host_id = target_event.data.host_id + except: + pass + sdk_bot_info = get_SDK_bot_info_from_Event(target_event) + api_obj = API.sendMessage(sdk_bot_info) + api_obj.headdata.vila_id = host_id + api_obj.data.room_id = chat_id + api_obj.data.msg_content = json.dumps(content) + api_obj.data.object_name = object_name + api_obj.do_api('POST') + res_data['data'] = {} + res_data['data']['chat_type'] = 'private' if False else 'group' + res_data['data']['chat_id'] = str(chat_id) + res_data['data']['object_name'] = str(object_name) + res_data['data']['content'] = str(json.dumps(content, ensure_ascii = False)) + return res_data + + +class inde_interface(OlivOS.API.inde_interface_T): + @OlivOS.API.Event.callbackLogger('mhyVila:create_message', ['chat_type', 'chat_id', 'object_name', 'content']) + def __create_message(target_event, chat_type:str, chat_id:str, object_name:str, content:dict, host_id=None, flag_log:bool=True): + res_data = None + if chat_type == 'group': + res_data = OlivOS.mhyVilaSDK.event_action.create_panel_message( + target_event = target_event, + chat_id = chat_id, + object_name = object_name, + content = content, + host_id = host_id + ) + return res_data + + def create_message( + self, + chat_type:str, + chat_id:str, + object_name:str, + content:dict, + host_id:'str|None' = None, + flag_log: bool = True, + remote: bool = False + ): + res_data = None + if remote: + pass + else: + res_data = inde_interface.__create_message( + self.event, + chat_type = chat_type, + chat_id = chat_id, + object_name = object_name, + content = content, + host_id = host_id, + flag_log = True + ) + return res_data + diff --git a/OlivOS/flaskServerAPI.py b/OlivOS/adapter/onebotV11/flaskServerAPI.py similarity index 81% rename from OlivOS/flaskServerAPI.py rename to OlivOS/adapter/onebotV11/flaskServerAPI.py index 5e43b557..021e0ab5 100644 --- a/OlivOS/flaskServerAPI.py +++ b/OlivOS/adapter/onebotV11/flaskServerAPI.py @@ -26,6 +26,8 @@ gCheckList = [ 'default', + 'shamrock_default', + 'para_default', 'gocqhttp', 'gocqhttp_hide', 'gocqhttp_show', @@ -34,7 +36,12 @@ 'gocqhttp_show_Android_Watch', 'gocqhttp_show_iPad', 'gocqhttp_show_iMac', - 'gocqhttp_show_old' + 'gocqhttp_show_old', + 'napcat', + 'napcat_hide', + 'napcat_show', + 'napcat_show_new', + 'napcat_show_old' ] class server(OlivOS.API.Proc_templet): @@ -63,8 +70,10 @@ def app(self): def set_config(self): with self.Proc_config['Flask_app'].app_context(): - @current_app.route(self.Proc_config['Flask_server_xpath'] + '///', - methods=self.Proc_config['Flask_server_methods']) + @current_app.route( + f"{self.Proc_config['Flask_server_xpath']}///", + methods=self.Proc_config['Flask_server_methods'] + ) def Flask_server_func(sdk_path, platform_path, model_path): sdk_event = OlivOS.onebotSDK.event(request.get_data(as_text=True)) sdk_event.platform['sdk'] = sdk_path @@ -81,8 +90,14 @@ def run(self): self.app() self.set_config() self.Proc_config['Flask_app'].config.from_object(self.Proc_config['config']) - self.log(2, OlivOS.L10NAPI.getTrans('OlivOS flask server [{0}] is running', [ - self.Proc_config['Flask_name'] + self.log(2, OlivOS.L10NAPI.getTrans('OlivOS flask server [{0}] is running on port [{1}]', [ + self.Proc_config['Flask_name'], + str(self.Proc_config['Flask_server_port']) + ], modelName + )) + self.log(2, OlivOS.L10NAPI.getTrans('OlivOS flask server [{0}] is running on [{1}]', [ + self.Proc_config['Flask_name'], + f"http://127.0.0.1:{self.Proc_config['Flask_server_port']}{self.Proc_config['Flask_server_xpath']}/qq/onebot/default" ], modelName )) if self.Proc_config['config'].debug_mode: diff --git a/OlivOS/onebotSDK.py b/OlivOS/adapter/onebotV11/onebotSDK.py similarity index 89% rename from OlivOS/onebotSDK.py rename to OlivOS/adapter/onebotV11/onebotSDK.py index 87f83a5e..5c3d6fac 100644 --- a/OlivOS/onebotSDK.py +++ b/OlivOS/adapter/onebotV11/onebotSDK.py @@ -19,8 +19,17 @@ import requests as req from urllib import parse import os +import time +import traceback + import OlivOS +paraMsgMap = [ + 'shamrock_default', + 'para_default' +] + +gFlagCheckList = [] class bot_info_T(object): def __init__(self, id=-1, host='', port=-1, access_token=None): @@ -53,24 +62,41 @@ def send_onebot_post_json(self): if type(self.bot_info) is not bot_info_T or self.bot_info.host == '' or self.bot_info.port == -1 or self.obj is None or self.node_ext == '': return None else: - json_str_tmp = json.dumps(obj=self.obj.__dict__) - send_url = self.bot_info.host + ':' + str( - self.bot_info.port) + '/' + self.node_ext + '?access_token=' + self.bot_info.access_token - - if self.bot_info.debug_mode: - if self.bot_info.debug_logger is not None: - self.bot_info.debug_logger.log(0, self.node_ext + ': ' + json_str_tmp) - - headers = { - 'Content-Type': 'application/json' - } - msg_res = req.request("POST", send_url, headers=headers, data=json_str_tmp) - - if self.bot_info.debug_mode: - if self.bot_info.debug_logger is not None: - self.bot_info.debug_logger.log(0, self.node_ext + ' - sendding succeed: ' + msg_res.text) - - return msg_res + try: + # clear_dict = {k: v for k, v in self.obj.__dict__.items() if v != -1} + clear_dict = self.obj.__dict__ + if clear_dict.get('message_type')=='private': + clear_dict.pop('group_id','No "group_id"') + json_str_tmp = json.dumps(obj=clear_dict, ensure_ascii=False) + tmp_host = self.bot_info.host + if tmp_host.startswith('http://') or tmp_host.startswith('https://'): + pass + else: + tmp_host = 'http://' + tmp_host + token_str = '' + token_dict = {} + if len(self.bot_info.access_token) > 0: + token_str = f'?access_token={self.bot_info.access_token}' + token_dict = {'Authorization': f'Bearer {self.bot_info.access_token}'} + send_url = f'{self.bot_info.host}:{self.bot_info.port}/{self.node_ext}{token_str}' + + if self.bot_info.debug_mode: + if self.bot_info.debug_logger is not None: + self.bot_info.debug_logger.log(0, self.node_ext + ': ' + json_str_tmp) + + headers = { + 'Content-Type': 'application/json' + } + headers.update(token_dict) + msg_res = req.request("POST", send_url, headers=headers, data=json_str_tmp.encode('utf-8')) + + if self.bot_info.debug_mode: + if self.bot_info.debug_logger is not None: + self.bot_info.debug_logger.log(0, self.node_ext + ' - sendding succeed: ' + msg_res.text) + + return msg_res + except: + traceback.print_exc() class api_templet(object): @@ -136,9 +162,32 @@ def event_load(self, raw): return res +def format_cq_code_msg(msg): + res = msg + if type(msg) is str: + res = msg + elif type(msg) is list: + res = '' + for msg_this in msg: + if type(msg_this) is dict \ + and 'type' in msg_this \ + and 'data' in msg_this \ + and type(msg_this['data']) is dict: + if msg_this['type'] == 'text': + if 'text' in msg_this['data']: + res += msg_this['data']['text'] + elif msg_this['type'] == 'at': + if 'qq' in msg_this['data']: + res += f"[CQ:at,qq={msg_this['data']['qq']}]" + else: + res += '[' + ','.join([f"CQ:{msg_this['type']}"] + [f"{key_this}={msg_this['data'][key_this]}" for key_this in msg_this['data']]) + ']' + return res + + # 支持OlivOS API事件生成的映射实现 def get_Event_from_SDK(target_event): - target_event.base_info['time'] = target_event.sdk_event.base_info['time'] + global gFlagCheckList + target_event.base_info['time'] = target_event.sdk_event.base_info.get('time', int(time.time())) target_event.base_info['self_id'] = str(target_event.sdk_event.base_info['self_id']) target_event.base_info['type'] = target_event.sdk_event.base_info['post_type'] target_event.platform['sdk'] = target_event.sdk_event.platform['sdk'] @@ -152,18 +201,16 @@ def get_Event_from_SDK(target_event): if target_event.sdk_event.json['message_type'] == 'private': target_event.active = True target_event.plugin_info['func_type'] = 'private_message_sent' + new_msg = format_cq_code_msg(target_event.sdk_event.json['message']) target_event.data = target_event.private_message_sent( str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['message'], + new_msg, target_event.sdk_event.json['sub_type'] ) - target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json['message']) + target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.message_id = str(target_event.sdk_event.json['message_id']) - target_event.data.raw_message = target_event.sdk_event.json['raw_message'] - target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'raw_message']) + target_event.data.raw_message = new_msg + target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.font = target_event.sdk_event.json['font'] target_event.data.sender.update(target_event.sdk_event.json['sender']) if 'user_id' in target_event.sdk_event.json['sender']: @@ -174,20 +221,17 @@ def get_Event_from_SDK(target_event): if target_event.sdk_event.json['sub_type'] == 'normal': target_event.active = True target_event.plugin_info['func_type'] = 'group_message_sent' + new_msg = format_cq_code_msg(target_event.sdk_event.json['message']) target_event.data = target_event.group_message_sent( str(target_event.sdk_event.json['group_id']), str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['message'], + new_msg, target_event.sdk_event.json['sub_type'] ) - target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'message']) + target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.message_id = str(target_event.sdk_event.json['message_id']) - target_event.data.raw_message = target_event.sdk_event.json['raw_message'] - target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'raw_message']) + target_event.data.raw_message = new_msg + target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.font = target_event.sdk_event.json['font'] target_event.data.sender.update(target_event.sdk_event.json['sender']) if 'user_id' in target_event.sdk_event.json['sender']: @@ -198,18 +242,16 @@ def get_Event_from_SDK(target_event): if target_event.sdk_event.json['message_type'] == 'private': target_event.active = True target_event.plugin_info['func_type'] = 'private_message' + new_msg = format_cq_code_msg(target_event.sdk_event.json['message']) target_event.data = target_event.private_message( str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['message'], + new_msg, target_event.sdk_event.json['sub_type'] ) - target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json['message']) + target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.message_id = str(target_event.sdk_event.json['message_id']) - target_event.data.raw_message = target_event.sdk_event.json['raw_message'] - target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'raw_message']) + target_event.data.raw_message = new_msg + target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.font = target_event.sdk_event.json['font'] target_event.data.sender.update(target_event.sdk_event.json['sender']) if 'user_id' in target_event.sdk_event.json['sender']: @@ -220,20 +262,17 @@ def get_Event_from_SDK(target_event): if target_event.sdk_event.json['sub_type'] == 'normal': target_event.active = True target_event.plugin_info['func_type'] = 'group_message' + new_msg = format_cq_code_msg(target_event.sdk_event.json['message']) target_event.data = target_event.group_message( str(target_event.sdk_event.json['group_id']), str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['message'], + new_msg, target_event.sdk_event.json['sub_type'] ) - target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'message']) + target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.message_id = str(target_event.sdk_event.json['message_id']) - target_event.data.raw_message = target_event.sdk_event.json['raw_message'] - target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'raw_message']) + target_event.data.raw_message = new_msg + target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.font = target_event.sdk_event.json['font'] target_event.data.sender.update(target_event.sdk_event.json['sender']) if 'user_id' in target_event.sdk_event.json['sender']: @@ -244,20 +283,17 @@ def get_Event_from_SDK(target_event): if target_event.sdk_event.json['sub_type'] == 'channel': target_event.active = True target_event.plugin_info['func_type'] = 'group_message' + new_msg = format_cq_code_msg(target_event.sdk_event.json['message']) target_event.data = target_event.group_message( str(target_event.sdk_event.json['channel_id']), str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['message'], + new_msg, target_event.sdk_event.json['sub_type'] ) - target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'message']) + target_event.data.message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.message_id = str(target_event.sdk_event.json['message_id']) - target_event.data.raw_message = target_event.sdk_event.json['message'] - target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', - target_event.sdk_event.json[ - 'message']) + target_event.data.raw_message = new_msg + target_event.data.raw_message_sdk = OlivOS.messageAPI.Message_templet('old_string', new_msg) target_event.data.font = None target_event.data.sender.update(target_event.sdk_event.json['sender']) if 'user_id' in target_event.sdk_event.json['sender']: @@ -313,7 +349,7 @@ def get_Event_from_SDK(target_event): target_event.plugin_info['func_type'] = 'group_member_increase' target_event.data = target_event.group_member_increase( str(target_event.sdk_event.json['group_id']), - str(target_event.sdk_event.json['operator_id']), + str(target_event.sdk_event.json.get('operator_id', '-1')), str(target_event.sdk_event.json['user_id']) ) if target_event.sdk_event.json['sub_type'] == 'approve': @@ -387,33 +423,37 @@ def get_Event_from_SDK(target_event): elif target_event.sdk_event.json['honor_type'] == 'emotion': target_event.data.type = 'emotion' elif target_event.base_info['type'] == 'request': - if target_event.sdk_event.json['request_type'] == 'friend': - target_event.active = True - target_event.plugin_info['func_type'] = 'friend_add_request' - target_event.data = target_event.friend_add_request( - str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['comment'] - ) - target_event.data.flag = target_event.sdk_event.json['flag'] - elif target_event.sdk_event.json['request_type'] == 'group': - if target_event.sdk_event.json['sub_type'] == 'add': - target_event.active = True - target_event.plugin_info['func_type'] = 'group_add_request' - target_event.data = target_event.group_add_request( - str(target_event.sdk_event.json['group_id']), - str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['comment'] - ) - target_event.data.flag = target_event.sdk_event.json['flag'] - elif target_event.sdk_event.json['sub_type'] == 'invite': - target_event.active = True - target_event.plugin_info['func_type'] = 'group_invite_request' - target_event.data = target_event.group_invite_request( - str(target_event.sdk_event.json['group_id']), - str(target_event.sdk_event.json['user_id']), - target_event.sdk_event.json['comment'] - ) - target_event.data.flag = target_event.sdk_event.json['flag'] + if 'flag' in target_event.sdk_event.json: + tmp_flag = target_event.sdk_event.json['flag'] + if tmp_flag not in gFlagCheckList: + gFlagCheckList.append(tmp_flag) + if target_event.sdk_event.json['request_type'] == 'friend': + target_event.active = True + target_event.plugin_info['func_type'] = 'friend_add_request' + target_event.data = target_event.friend_add_request( + str(target_event.sdk_event.json['user_id']), + target_event.sdk_event.json['comment'] + ) + target_event.data.flag = tmp_flag + elif target_event.sdk_event.json['request_type'] == 'group': + if target_event.sdk_event.json['sub_type'] == 'add': + target_event.active = True + target_event.plugin_info['func_type'] = 'group_add_request' + target_event.data = target_event.group_add_request( + str(target_event.sdk_event.json['group_id']), + str(target_event.sdk_event.json['user_id']), + target_event.sdk_event.json['comment'] + ) + target_event.data.flag = tmp_flag + elif target_event.sdk_event.json['sub_type'] == 'invite': + target_event.active = True + target_event.plugin_info['func_type'] = 'group_invite_request' + target_event.data = target_event.group_invite_request( + str(target_event.sdk_event.json['group_id']), + str(target_event.sdk_event.json['user_id']), + target_event.sdk_event.json['comment'] + ) + target_event.data.flag = tmp_flag elif target_event.base_info['type'] == 'meta_event': if target_event.sdk_event.json['meta_event_type'] == 'lifecycle': target_event.active = True @@ -432,7 +472,7 @@ def get_Event_from_SDK(target_event): target_event.sdk_event.json['interval'] ) -def formatMessage(data:str): +def formatMessage(data:str, msgType:str = 'para'): res = data data_obj = OlivOS.messageAPI.Message_templet( mode_rx = 'old_string', @@ -452,7 +492,28 @@ def formatMessage(data:str): file_path = OlivOS.contentAPI.resourcePathTransform('images', file_path) if os.path.exists(file_path): data_obj_this.data['file'] = 'file:///%s' % file_path - res = data_obj.get('old_string') + if msgType == 'para': + res = paraMapper(paraList = data_obj.data, msgType = 'para') + else: + res = data_obj.get('old_string') + return res + + +def paraMapper(paraList, msgType='para'): + res = [] + if 'para' == msgType: + for para in paraList: + tmp_para = para.__dict__ + if para.type == 'at': + tmp_para = {} + tmp_para['type'] = 'at' + tmp_para['data'] = {} + tmp_para['data']['qq'] = para.data['id'] + res.append(tmp_para) + elif 'msg' == msgType: + res = '' + for para in paraList: + res += para.CQ() return res # 支持OlivOS API调用的方法实现 @@ -472,17 +533,25 @@ def reply_group_msg(target_event, message): ) def send_private_msg(target_event, user_id, message): + global paraMsgMap + msgType = 'msg' this_msg = api.send_msg(get_SDK_bot_info_from_Event(target_event)) this_msg.data.message_type = 'private' this_msg.data.user_id = str(user_id) - this_msg.data.message = formatMessage(message) + if target_event.bot_info.platform['model'] in paraMsgMap: + msgType = 'para' + this_msg.data.message = formatMessage(data = message, msgType = msgType) this_msg.do_api() def send_group_msg(target_event, group_id, message): + global paraMsgMap + msgType = 'msg' this_msg = api.send_msg(get_SDK_bot_info_from_Event(target_event)) this_msg.data.message_type = 'group' this_msg.data.group_id = str(group_id) - this_msg.data.message = formatMessage(message) + if target_event.bot_info.platform['model'] in paraMsgMap: + msgType = 'para' + this_msg.data.message = formatMessage(data = message, msgType = msgType) this_msg.do_api() def delete_msg(target_event, message_id): diff --git a/OlivOS/onebotV12LinkServerAPI.py b/OlivOS/adapter/onebotV12/onebotV12LinkServerAPI.py similarity index 100% rename from OlivOS/onebotV12LinkServerAPI.py rename to OlivOS/adapter/onebotV12/onebotV12LinkServerAPI.py diff --git a/OlivOS/onebotV12SDK.py b/OlivOS/adapter/onebotV12/onebotV12SDK.py similarity index 100% rename from OlivOS/onebotV12SDK.py rename to OlivOS/adapter/onebotV12/onebotV12SDK.py diff --git a/OlivOS/qqGuildLinkServerAPI.py b/OlivOS/adapter/qqGuild/qqGuildLinkServerAPI.py similarity index 100% rename from OlivOS/qqGuildLinkServerAPI.py rename to OlivOS/adapter/qqGuild/qqGuildLinkServerAPI.py diff --git a/OlivOS/qqGuildSDK.py b/OlivOS/adapter/qqGuild/qqGuildSDK.py similarity index 100% rename from OlivOS/qqGuildSDK.py rename to OlivOS/adapter/qqGuild/qqGuildSDK.py diff --git a/OlivOS/qqGuildv2LinkServerAPI.py b/OlivOS/adapter/qqGuild/qqGuildv2LinkServerAPI.py similarity index 100% rename from OlivOS/qqGuildv2LinkServerAPI.py rename to OlivOS/adapter/qqGuild/qqGuildv2LinkServerAPI.py diff --git a/OlivOS/qqGuildv2SDK.py b/OlivOS/adapter/qqGuild/qqGuildv2SDK.py similarity index 97% rename from OlivOS/qqGuildv2SDK.py rename to OlivOS/adapter/qqGuild/qqGuildv2SDK.py index 210ea446..1c5dce34 100644 --- a/OlivOS/qqGuildv2SDK.py +++ b/OlivOS/adapter/qqGuild/qqGuildv2SDK.py @@ -75,22 +75,23 @@ class intents_T(IntEnum): class bot_info_T(object): - def __init__(self, id=-1, access_token=None, model='private'): + def __init__(self, id=-1, access_token=None, model='private', intents=0): self.id = id self.access_token = access_token self.model = model + self.intents = intents self.debug_mode = False self.debug_logger = None def get_SDK_bot_info_from_Plugin_bot_info(plugin_bot_info): res = bot_info_T( - plugin_bot_info.id, - plugin_bot_info.post_info.access_token + id = plugin_bot_info.id, + access_token = plugin_bot_info.post_info.access_token, + model = plugin_bot_info.platform.get('model', 'private'), + intents = plugin_bot_info.post_info.port ) res.debug_mode = plugin_bot_info.debug_mode - if plugin_bot_info.platform['model'] == 'public': - res.model = 'public' return res @@ -175,14 +176,18 @@ def __init__(self, data): payload_template.__init__(self, data, True) class sendIdentify(payload_template): - def __init__(self, bot_info, intents=(int(intents_T.GUILDS) | int(intents_T.DIRECT_MESSAGE))): + def __init__(self, bot_info:bot_info_T, intents=(int(intents_T.GUILDS) | int(intents_T.DIRECT_MESSAGE))): tmp_intents = intents - if bot_info.model == 'private': + if bot_info.model in ['private']: tmp_intents |= int(intents_T.GUILD_MESSAGES) #tmp_intents |= int(intents_T.QQ_MESSAGES) - elif bot_info.model == 'public': + elif bot_info.model in ['public', 'sandbox']: tmp_intents |= int(intents_T.PUBLIC_GUILD_MESSAGES) tmp_intents |= int(intents_T.PUBLIC_QQ_MESSAGES) + elif bot_info.model in ['public_guild_only']: + tmp_intents |= int(intents_T.PUBLIC_GUILD_MESSAGES) + elif bot_info.model in ['private_intents', 'public_intents', 'sandbox_intents']: + tmp_intents = bot_info.intents payload_template.__init__(self) self.data.op = 2 try: @@ -227,8 +232,15 @@ def __init__(self): self.route = None self.res = None + def __switch_host(self): + global sdkAPIHost + if self.bot_info.model in ['sandbox', 'sandbox_intents']: + if self.host == sdkAPIHost['default']: + self.host = sdkAPIHost['sandbox'] + def do_api_plant(self, req_type='POST'): try: + self.__switch_host() tmp_payload_dict = {} tmp_sdkAPIRouteTemp = sdkAPIRouteTemp.copy() if self.metadata is not None: @@ -259,6 +271,7 @@ def do_api_plant(self, req_type='POST'): def do_api(self, req_type='POST'): try: + self.__switch_host() tmp_payload_dict = {} tmp_sdkAPIRouteTemp = sdkAPIRouteTemp.copy() if self.metadata is not None: diff --git a/OlivOS/qqRedLinkServerAPI.py b/OlivOS/adapter/red/qqRedLinkServerAPI.py similarity index 100% rename from OlivOS/qqRedLinkServerAPI.py rename to OlivOS/adapter/red/qqRedLinkServerAPI.py diff --git a/OlivOS/qqRedSDK.py b/OlivOS/adapter/red/qqRedSDK.py similarity index 100% rename from OlivOS/qqRedSDK.py rename to OlivOS/adapter/red/qqRedSDK.py diff --git a/OlivOS/telegramPollServerAPI.py b/OlivOS/adapter/telegram/telegramPollServerAPI.py similarity index 100% rename from OlivOS/telegramPollServerAPI.py rename to OlivOS/adapter/telegram/telegramPollServerAPI.py diff --git a/OlivOS/telegramSDK.py b/OlivOS/adapter/telegram/telegramSDK.py similarity index 98% rename from OlivOS/telegramSDK.py rename to OlivOS/adapter/telegram/telegramSDK.py index e8469660..e00417f4 100644 --- a/OlivOS/telegramSDK.py +++ b/OlivOS/adapter/telegram/telegramSDK.py @@ -7,7 +7,7 @@ \____/ /_____/___/ _____/ \____/ /____/ @File : OlivOS/telegramSDK.py -@Author : lunzhiPenxil仑质 +@Author : lunzhiPenxil仑质, MetaLeo元理 @Contact : lunzhipenxil@gmail.com @License : AGPL @Copyright : (C) 2020-2023, OlivOS-Team @@ -222,12 +222,20 @@ def get_message_obj_from_SDK(target_event): checkInDictSafe('photo', target_event.sdk_event.json, ['message']) ]): message_list = [] - if type(target_event.sdk_event.json['message']['photo']) == list: + if type(target_event.sdk_event.json['message']['photo']) is list: message_list.append( OlivOS.messageAPI.PARA.image( target_event.sdk_event.json['message']['photo'][0]['file_id'] ) ) + if checkByListAnd([ + checkInDictSafe('caption', target_event.sdk_event.json, ['message']) + ]): + message_list.append( + OlivOS.messageAPI.PARA.text( + text = str(target_event.sdk_event.json['message']['caption']) + ) + ) message_obj = OlivOS.messageAPI.Message_templet( 'olivos_para', message_list @@ -288,7 +296,7 @@ def get_Event_from_SDK(target_event): checkInDictSafe('message_id', target_event.sdk_event.json, ['message']), checkInDictSafe('from', target_event.sdk_event.json, ['message']), checkInDictSafe('first_name', target_event.sdk_event.json, ['message', 'from']), - checkInDictSafe('text', target_event.sdk_event.json, ['message']), + #checkInDictSafe('text', target_event.sdk_event.json, ['message']), checkEquelInDictSafe('private', target_event.sdk_event.json, ['message', 'chat', 'type']) ]): message_obj = None @@ -324,7 +332,7 @@ def get_Event_from_SDK(target_event): checkInDictSafe('from', target_event.sdk_event.json, ['message']), checkInDictSafe('id', target_event.sdk_event.json, ['message', 'from']), checkInDictSafe('first_name', target_event.sdk_event.json, ['message', 'from']), - checkInDictSafe('text', target_event.sdk_event.json, ['message']), + #checkInDictSafe('text', target_event.sdk_event.json, ['message']), checkEquelInDictSafe('group', target_event.sdk_event.json, ['message', 'chat', 'type']) ]): message_obj = None @@ -362,7 +370,7 @@ def get_Event_from_SDK(target_event): checkInDictSafe('from', target_event.sdk_event.json, ['message']), checkInDictSafe('id', target_event.sdk_event.json, ['message', 'from']), checkInDictSafe('first_name', target_event.sdk_event.json, ['message', 'from']), - checkInDictSafe('text', target_event.sdk_event.json, ['message']), + #checkInDictSafe('text', target_event.sdk_event.json, ['message']), checkEquelInDictSafe('supergroup', target_event.sdk_event.json, ['message', 'chat', 'type']) ]): message_obj = None diff --git a/OlivOS/virtualTerminalLinkServerAPI.py b/OlivOS/adapter/virtualTerminal/virtualTerminalLinkServerAPI.py similarity index 96% rename from OlivOS/virtualTerminalLinkServerAPI.py rename to OlivOS/adapter/virtualTerminal/virtualTerminalLinkServerAPI.py index 738e5473..a9f82789 100644 --- a/OlivOS/virtualTerminalLinkServerAPI.py +++ b/OlivOS/adapter/virtualTerminal/virtualTerminalLinkServerAPI.py @@ -14,6 +14,7 @@ @Desc : None ''' +import gevent from gevent import pywsgi from flask import Flask from flask import current_app @@ -139,10 +140,13 @@ def Flask_server_func(): res = json.dumps(self.Proc_data['reply_event_pool'][event_id]) self.Proc_data['reply_event_pool'].pop(event_id) break - time.sleep(0.25) + gevent.sleep(0.25) return res, status, header - server = pywsgi.WSGIServer(('0.0.0.0', self.Proc_data['bot_info_dict'].post_info.port), - self.Proc_config['Flask_app'], log=None) + server = pywsgi.WSGIServer( + ('0.0.0.0', self.Proc_data['bot_info_dict'].post_info.port), + self.Proc_config['Flask_app'], + log=None + ) server.serve_forever() def send_init_event(self): diff --git a/OlivOS/virtualTerminalSDK.py b/OlivOS/adapter/virtualTerminal/virtualTerminalSDK.py similarity index 100% rename from OlivOS/virtualTerminalSDK.py rename to OlivOS/adapter/virtualTerminal/virtualTerminalSDK.py diff --git a/OlivOS/L10NAPI.py b/OlivOS/core/L10N/L10NAPI.py similarity index 100% rename from OlivOS/L10NAPI.py rename to OlivOS/core/L10N/L10NAPI.py diff --git a/OlivOS/L10NDataAPI.py b/OlivOS/core/L10N/L10NDataAPI.py similarity index 70% rename from OlivOS/L10NDataAPI.py rename to OlivOS/core/L10N/L10NDataAPI.py index 47f8c10c..1b1b51f3 100644 --- a/OlivOS/L10NDataAPI.py +++ b/OlivOS/core/L10N/L10NDataAPI.py @@ -56,6 +56,12 @@ 'hackChatLinkServerAPI_0004': 'OlivOS hackChat link server [{0}] websocket link close', 'hackChatLinkServerAPI_0005': 'OlivOS hackChat link server [{0}] websocket link start', 'hackChatLinkServerAPI_0006': 'OlivOS hackChat link server [{0}] websocket link lost', + 'OPQBotLinkServerAPI_0001': 'OlivOS OPQBot link server [{0}] is running on [{1}]', + 'OPQBotLinkServerAPI_0002': 'OlivOS OPQBot link server [{0}] websocket link will retry in {1}s', + 'OPQBotLinkServerAPI_0003': 'OlivOS OPQBot link server [{0}] websocket link error', + 'OPQBotLinkServerAPI_0004': 'OlivOS OPQBot link server [{0}] websocket link close', + 'OPQBotLinkServerAPI_0005': 'OlivOS OPQBot link server [{0}] websocket link start', + 'OPQBotLinkServerAPI_0006': 'OlivOS OPQBot link server [{0}] websocket link lost', 'updateAPI_0001': 'will check {0} lib after {1}s ...', 'updateAPI_0002': 'check {0} lib patch target md5: [{1}]', 'updateAPI_0003': 'check {0} lib patch [{1}] md5: [{2}]', @@ -80,19 +86,38 @@ 'libWQEXEModelAPI_0002': 'OlivOS libWQEXEModel server [{0}] will run under visiable mode', 'libWQEXEModelAPI_0003': 'OlivOS libWQEXEModel server [{0}] will retry in 10s...', 'libWQEXEModelAPI_0004': 'OlivOS libWQEXEModel failed: {0}\n{1}', + 'libOPQBotEXEModelAPI_0001': 'OlivOS libOPQBotEXEModel server [{0}] can`t found target lib', + 'libOPQBotEXEModelAPI_0002': 'OlivOS libOPQBotEXEModel server [{0}] will run under visiable mode', + 'libOPQBotEXEModelAPI_0003': 'OlivOS libOPQBotEXEModel server [{0}] will retry in 10s...', + 'libOPQBotEXEModelAPI_0004': 'OlivOS libOPQBotEXEModel failed: {0}\n{1}', 'libEXEModelAPI_0001': 'OlivOS libEXEModel server [{0}] can`t found target lib', 'libEXEModelAPI_0002': 'OlivOS libEXEModel server [{0}] will run under visiable mode', 'libEXEModelAPI_0003': 'OlivOS libEXEModel server [{0}] will retry in 10s...', 'libEXEModelAPI_0004': 'OlivOS libEXEModel failed: {0}\n{1}', 'libEXEModelAPI_0005': 'OlivOS libEXEModel server [{0}] is running', 'libEXEModelAPI_0006': 'OlivOS libEXEModel server [{0}] exited', - 'flaskServerAPI_0001': 'OlivOS flask server [{0}] is running', + 'libEXEModelAPI_0007': 'OlivOS libEXEModel server [{0}] will run in {1}s...', + 'libNapCatEXEModelAPI_0001': 'OlivOS libNapCatEXEModel server [{0}] can`t found target lib', + 'libNapCatEXEModelAPI_0002': 'OlivOS libNapCatEXEModel server [{0}] will run under visiable mode', + 'libNapCatEXEModelAPI_0003': 'OlivOS libNapCatEXEModel server [{0}] will retry in 10s...', + 'libNapCatEXEModelAPI_0004': 'OlivOS libNapCatEXEModel failed: {0}\n{1}', + 'libNapCatEXEModelAPI_0005': 'OlivOS libNapCatEXEModel server [{0}] is running', + 'libNapCatEXEModelAPI_0006': 'OlivOS libNapCatEXEModel server [{0}] exited', + 'flaskServerAPI_0001': 'OlivOS flask server [{0}] is running on port [{1}]', + 'flaskServerAPI_0002': 'OlivOS flask server [{0}] is running on [{1}]', 'libCWCBEXEModelAPI_0001': 'OlivOS libCWCBEXEModel server [{0}] can`t found target lib', 'libCWCBEXEModelAPI_0002': 'OlivOS libCWCBEXEModel server [{0}] will run under visiable mode', 'libCWCBEXEModelAPI_0003': 'OlivOS libCWCBEXEModel server [{0}] will retry in 10s...', 'libCWCBEXEModelAPI_0004': 'OlivOS libCWCBEXEModel failed: {0}\n{1}', 'libCWCBEXEModelAPI_0005': 'OlivOS libCWCBEXEModel server [{0}] is running', 'libCWCBEXEModelAPI_0006': 'OlivOS libCWCBEXEModel server [{0}] exited', + 'libAstralQsignEXEModelAPI_0001': 'OlivOS libAstralQsignEXEModel server [{0}] can`t found target lib', + 'libAstralQsignEXEModelAPI_0002': 'OlivOS libAstralQsignEXEModel server [{0}] will run under visiable mode', + 'libAstralQsignEXEModelAPI_0003': 'OlivOS libAstralQsignEXEModel server [{0}] will retry in 10s...', + 'libAstralQsignEXEModelAPI_0004': 'OlivOS libAstralQsignEXEModel failed: {0}\n{1}', + 'libAstralQsignEXEModelAPI_0005': 'OlivOS libAstralQsignEXEModel server [{0}] is running', + 'libAstralQsignEXEModelAPI_0006': 'OlivOS libAstralQsignEXEModel server [{0}] exited', + 'libAstralQsignEXEModelAPI_0007': 'OlivOS libAstralQsignEXEModel server [{0}] is running on port [{1}]', 'bootAPI_0001': 'OlivOS model [{0}] will init', 'bootAPI_0002': 'OlivOS model [{0}] init', 'bootAPI_0003': 'OlivOS model [{0}] will try init', @@ -136,6 +161,12 @@ 'hackChatLinkServerAPI_0004': 'OlivOS hackChat 连接服务组件 [{0}] WebSocket 连接 已经关闭', 'hackChatLinkServerAPI_0005': 'OlivOS hackChat 连接服务组件 [{0}] WebSocket 连接 已经启动', 'hackChatLinkServerAPI_0006': 'OlivOS hackChat 连接服务组件 [{0}] WebSocket 连接 已经丢失', + 'OPQBotLinkServerAPI_0001': 'OlivOS OPQBot 连接服务组件 [{0}] 正在运作, 采用WS服务器 [{1}]', + 'OPQBotLinkServerAPI_0002': 'OlivOS OPQBot 连接服务组件 [{0}] WebSocket 连接 将在{1}秒内重试', + 'OPQBotLinkServerAPI_0003': 'OlivOS OPQBot 连接服务组件 [{0}] WebSocket 连接 发生错误', + 'OPQBotLinkServerAPI_0004': 'OlivOS OPQBot 连接服务组件 [{0}] WebSocket 连接 已经关闭', + 'OPQBotLinkServerAPI_0005': 'OlivOS OPQBot 连接服务组件 [{0}] WebSocket 连接 已经启动', + 'OPQBotLinkServerAPI_0006': 'OlivOS OPQBot 连接服务组件 [{0}] WebSocket 连接 已经丢失', 'updateAPI_0001': '将在 {1}秒后 检查 {0} 依赖库 ...', 'updateAPI_0002': '检查 {0} 依赖库补丁 目标 MD5文件摘要: [{1}]', 'updateAPI_0003': '检查 {0} 依赖库补丁 [{1}] MD5文件摘要: [{2}]', @@ -160,19 +191,38 @@ 'libWQEXEModelAPI_0002': 'OlivOS WQ进程托管服务组件 [{0}] 将在前台模式下运行', 'libWQEXEModelAPI_0003': 'OlivOS WQ进程托管服务组件 [{0}] 将在10秒后重试...', 'libWQEXEModelAPI_0004': 'OlivOS WQ进程托管服务组件 错误: {0}\n{1}', + 'libOPQBotEXEModelAPI_0001': 'OlivOS OPQBot进程托管服务组件 [{0}] 无法找到库文件', + 'libOPQBotEXEModelAPI_0002': 'OlivOS OPQBot进程托管服务组件 [{0}] 将在前台模式下运行', + 'libOPQBotEXEModelAPI_0003': 'OlivOS OPQBot进程托管服务组件 [{0}] 将在10秒后重试...', + 'libOPQBotEXEModelAPI_0004': 'OlivOS OPQBot进程托管服务组件 错误: {0}\n{1}', 'libEXEModelAPI_0001': 'OlivOS GoCq进程托管服务组件 [{0}] 无法找到库文件', 'libEXEModelAPI_0002': 'OlivOS GoCq进程托管服务组件 [{0}] 将在前台模式下运行', 'libEXEModelAPI_0003': 'OlivOS GoCq进程托管服务组件 [{0}] 将在10秒后重试...', 'libEXEModelAPI_0004': 'OlivOS GoCq进程托管服务组件 错误: {0}\n{1}', 'libEXEModelAPI_0005': 'OlivOS GoCq进程托管服务组件 [{0}] 正在运作', 'libEXEModelAPI_0006': 'OlivOS GoCq进程托管服务组件 [{0}] 已经存在', - 'flaskServerAPI_0001': 'OlivOS onebotV11 flask POST服务组件 [{0}] 正在运作', + 'libEXEModelAPI_0007': 'OlivOS GoCq进程托管服务组件 [{0}] 将在{1}秒内启动...', + 'libNapCatEXEModelAPI_0001': 'OlivOS NapCat进程托管服务组件 [{0}] 无法找到库文件', + 'libNapCatEXEModelAPI_0002': 'OlivOS NapCat进程托管服务组件 [{0}] 将在前台模式下运行', + 'libNapCatEXEModelAPI_0003': 'OlivOS NapCat进程托管服务组件 [{0}] 将在10秒后重试...', + 'libNapCatEXEModelAPI_0004': 'OlivOS NapCat进程托管服务组件 错误: {0}\n{1}', + 'libNapCatEXEModelAPI_0005': 'OlivOS NapCat进程托管服务组件 [{0}] 正在运作', + 'libNapCatEXEModelAPI_0006': 'OlivOS NapCat进程托管服务组件 [{0}] 已经存在', + 'flaskServerAPI_0001': 'OlivOS onebotV11 flask POST服务组件 [{0}] 正在运作于端口 [{1}]', + 'flaskServerAPI_0002': 'OlivOS onebotV11 flask POST服务组件 [{0}] 的参考上报地址为 [{1}]', 'libCWCBEXEModelAPI_0001': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 无法找到库文件', 'libCWCBEXEModelAPI_0002': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 将在前台模式下运行', 'libCWCBEXEModelAPI_0003': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 将在10秒后重试...', 'libCWCBEXEModelAPI_0004': 'OlivOS ComWeChatBotClient进程托管服务组件 错误: {0}\n{1}', 'libCWCBEXEModelAPI_0005': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 正在运作', 'libCWCBEXEModelAPI_0006': 'OlivOS ComWeChatBotClient进程托管服务组件 [{0}] 已经存在', + 'libAstralQsignEXEModelAPI_0001': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 无法找到库文件', + 'libAstralQsignEXEModelAPI_0002': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 将在前台模式下运行', + 'libAstralQsignEXEModelAPI_0003': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 将在10秒后重试...', + 'libAstralQsignEXEModelAPI_0004': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 错误: {0}\n{1}', + 'libAstralQsignEXEModelAPI_0005': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 正在运作', + 'libAstralQsignEXEModelAPI_0006': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 已经存在', + 'libAstralQsignEXEModelAPI_0007': 'OlivOS libAstralQsignEXEModelAPI进程托管服务组件 [{0}] 正在运作于端口 [{1}]', 'bootAPI_0001': 'OlivOS 组件 [{0}] 即将初始化', 'bootAPI_0002': 'OlivOS 组件 [{0}] 初始化', 'bootAPI_0003': 'OlivOS 组件 [{0}] 即将尝试初始化', diff --git a/OlivOS/bootAPI.py b/OlivOS/core/boot/bootAPI.py similarity index 88% rename from OlivOS/bootAPI.py rename to OlivOS/core/boot/bootAPI.py index 44863cbf..b6b8b2a7 100644 --- a/OlivOS/bootAPI.py +++ b/OlivOS/core/boot/bootAPI.py @@ -519,8 +519,32 @@ def start(self): bot_info_dict=plugin_bot_info_dict[bot_info_key], debug_mode=False ) - Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity( - tmp_proc_mode) + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity(tmp_proc_mode) + elif basic_conf_models_this['type'] == 'OPQBot_link': + flag_need_enable = False + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['sdk'] == 'onebot': + if plugin_bot_info_dict[bot_info_key].platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + flag_need_enable = True + if not flag_need_enable: + continue + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['sdk'] == 'onebot': + if plugin_bot_info_dict[bot_info_key].platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + tmp_Proc_name = basic_conf_models_this['name'] + '=' + bot_info_key + tmp_queue_name = basic_conf_models_this['rx_queue'] + '=' + bot_info_key + multiprocessing_dict[tmp_queue_name] = multiprocessing.Queue() + Proc_dict[tmp_Proc_name] = OlivOS.OPQBotLinkServerAPI.server( + Proc_name=tmp_Proc_name, + scan_interval=basic_conf_models_this['interval'], + dead_interval=basic_conf_models_this['dead_interval'], + rx_queue=multiprocessing_dict[tmp_queue_name], + tx_queue=multiprocessing_dict[basic_conf_models_this['tx_queue']], + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + bot_info_dict=plugin_bot_info_dict[bot_info_key], + debug_mode=False + ) + 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 for bot_info_key in plugin_bot_info_dict: @@ -724,9 +748,55 @@ def start(self): 'tmp_proc_mode': tmp_proc_mode } ).start() + elif basic_conf_models_this['type'] == 'opqbot_lib_exe_model': + if platform.system() == 'Windows': + threading.Thread( + target = OlivOS.libOPQBotEXEModelAPI.startOPQBotLibExeModel, + kwargs = { + 'plugin_bot_info_dict': plugin_bot_info_dict, + 'basic_conf_models_this': basic_conf_models_this, + 'multiprocessing_dict': multiprocessing_dict, + 'Proc_dict': Proc_dict, + 'Proc_Proc_dict': Proc_Proc_dict, + 'basic_conf_models': basic_conf_models, + 'tmp_proc_mode': tmp_proc_mode + } + ).start() + elif basic_conf_models_this['type'] == 'napcat_lib_exe_model': + if platform.system() == 'Windows': + threading.Thread( + target = OlivOS.libNapCatEXEModelAPI.startNapCatLibExeModel, + kwargs = { + 'plugin_bot_info_dict': plugin_bot_info_dict, + 'basic_conf_models_this': basic_conf_models_this, + 'multiprocessing_dict': multiprocessing_dict, + 'Proc_dict': Proc_dict, + 'Proc_Proc_dict': Proc_Proc_dict, + 'basic_conf_models': basic_conf_models, + 'tmp_proc_mode': tmp_proc_mode + } + ).start() + elif basic_conf_models_this['type'] == 'astralqsign_lib_exe_model': + if platform.system() == 'Windows': + if not OlivOS.libAstralQsignEXEModelAPI.isBotActive(plugin_bot_info_dict): + continue + threading.Thread( + target = OlivOS.libAstralQsignEXEModelAPI.startAstralQsignLibExeModel, + kwargs = { + 'plugin_bot_info_dict': plugin_bot_info_dict, + 'basic_conf_models_this': basic_conf_models_this, + 'multiprocessing_dict': multiprocessing_dict, + 'Proc_dict': Proc_dict, + 'Proc_Proc_dict': Proc_Proc_dict, + 'basic_conf_models': basic_conf_models, + 'tmp_proc_mode': tmp_proc_mode + } + ).start() elif basic_conf_models_this['type'] == 'update_get': - threading.Thread(target=update_get_func, - args=(Proc_dict, basic_conf_models, basic_conf_models_this)).start() + threading.Thread( + target=update_get_func, + args=(Proc_dict, basic_conf_models, basic_conf_models_this) + ).start() elif basic_conf_models_this['type'] == 'update_replace': OlivOS.updateAPI.OlivOSUpdateReplace( logger_proc=Proc_dict[basic_conf_models_this['logger_proc']] @@ -761,7 +831,6 @@ def start(self): 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 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']: @@ -821,6 +890,39 @@ def start(self): ) 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 == 'init_type_open_tx_turingTest_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 'tx_turingTest_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.libEXEModelAPI.txTuringTestPage( + 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 \ diff --git a/OlivOS/bootDataAPI.py b/OlivOS/core/boot/bootDataAPI.py similarity index 85% rename from OlivOS/bootDataAPI.py rename to OlivOS/core/boot/bootDataAPI.py index 8fbece2b..dcd37841 100644 --- a/OlivOS/bootDataAPI.py +++ b/OlivOS/core/boot/bootDataAPI.py @@ -29,10 +29,14 @@ "OlivOS_account_config_save", "OlivOS_account_config", "OlivOS_nativeWinUIAPI", + "OlivOS_astralqsign_lib_exe_model", "OlivOS_gocqhttp_lib_exe_model", "OlivOS_walleq_lib_exe_model", "OlivOS_cwcb_lib_exe_model", + "OlivOS_opqbot_lib_exe_model", + "OlivOS_napcat_lib_exe_model", "OlivOS_hackChat_link", + "OlivOS_OPQBot_link", "OlivOS_qqRed_link", "OlivOS_dingtalk_link", "OlivOS_plugin", @@ -72,10 +76,14 @@ "OlivOS_account_config_update" ], "account_update": [ + "OlivOS_astralqsign_lib_exe_model", "OlivOS_gocqhttp_lib_exe_model", "OlivOS_walleq_lib_exe_model", "OlivOS_cwcb_lib_exe_model", + "OlivOS_opqbot_lib_exe_model", + "OlivOS_napcat_lib_exe_model", "OlivOS_hackChat_link", + "OlivOS_OPQBot_link", "OlivOS_qqRed_link", "OlivOS_dingtalk_link", "OlivOS_virtual_terminal_link", @@ -94,10 +102,14 @@ }, "type_event": { "account_update": [ + "astralqsign_lib_exe_model", "gocqhttp_lib_exe_model", "walleq_lib_exe_model", "cwcb_lib_exe_model", + "opqbot_lib_exe_model", + "napcat_lib_exe_model", "hackChat_link", + "OPQBot_link", "qqRed_link", "dingtalk_link", "terminal_link", @@ -127,8 +139,11 @@ "OlivOS_gocqhttp_lib_rx_queue", "OlivOS_walleq_lib_rx_queue", "OlivOS_cwcb_lib_rx_queue", + "OlivOS_opqbot_lib_rx_queue", + "OlivOS_napcat_lib_rx_queue", "OlivOS_virtual_terminal_queue", "OlivOS_hackChat_queue", + "OlivOS_OPQBot_queue", "OlivOS_biliLive_queue", "OlivOS_onebotv12_queue", "OlivOS_qqRed_queue", @@ -329,6 +344,17 @@ "logger_proc": "OlivOS_logger", "debug": False }, + "OlivOS_OPQBot_link": { + "enable": True, + "name": "OlivOS_OPQBot_link", + "type": "OPQBot_link", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_OPQBot_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": False + }, "OlivOS_qqRed_link": { "enable": True, "name": "OlivOS_qqRed_link", @@ -455,6 +481,7 @@ "tx_queue": "OlivOS_rx_queue", "logger_proc": "OlivOS_logger", "target_proc": "OlivOS_flask_post_rx", + "sub_target_proc": "OlivOS_astralqsign_lib_exe_model", "control_queue": "OlivOS_control_queue", "debug": False }, @@ -484,6 +511,47 @@ "control_queue": "OlivOS_control_queue", "debug": False }, + "OlivOS_opqbot_lib_exe_model": { + "enable": True, + "name": "OlivOS_opqbot_lib_exe_model", + "type": "opqbot_lib_exe_model", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_opqbot_lib_rx_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "target_proc": None, + "control_queue": "OlivOS_control_queue", + "debug": False + }, + "OlivOS_napcat_lib_exe_model": { + "enable": True, + "name": "OlivOS_napcat_lib_exe_model", + "type": "napcat_lib_exe_model", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_napcat_lib_rx_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "target_proc": "OlivOS_flask_post_rx", + "control_queue": "OlivOS_control_queue", + "debug": False + }, + "OlivOS_astralqsign_lib_exe_model": { + "enable": True, + "name": "OlivOS_astralqsign_lib_exe_model", + "type": "astralqsign_lib_exe_model", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "control_queue": "OlivOS_control_queue", + "debug": False, + "server": { + "auto": True, + "host": "0.0.0.0", + "port": 55011, + "token": "114514" + } + }, "OlivOS_webview_page": { "enable": True, "name": "OlivOS_webview_page", @@ -494,6 +562,16 @@ "control_queue": "OlivOS_control_queue", "debug": False }, + "OlivOS_tx_turingTest_webview_page": { + "enable": True, + "name": "OlivOS_tx_turingTest_webview_page", + "type": "tx_turingTest_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/API.py b/OlivOS/core/core/API.py similarity index 97% rename from OlivOS/API.py rename to OlivOS/core/core/API.py index 920c41af..ccf34e87 100644 --- a/OlivOS/API.py +++ b/OlivOS/core/core/API.py @@ -169,6 +169,8 @@ def __init_inde_interface(self): self.indeAPI = inde_interface_T(self, self.platform['platform']) if self.platform['sdk'] == 'kaiheila_link': self.indeAPI = OlivOS.kaiheilaSDK.inde_interface(self, self.platform['platform']) + if self.platform['sdk'] == 'mhyVila_link': + self.indeAPI = OlivOS.mhyVilaSDK.inde_interface(self, self.platform['platform']) def get_Event_from_SDK(self): if self.sdk_event_type is OlivOS.virtualTerminalSDK.event: @@ -203,6 +205,8 @@ def get_Event_from_SDK(self): OlivOS.qqRedSDK.get_Event_from_SDK(self) elif self.sdk_event_type is OlivOS.hackChatSDK.event: OlivOS.hackChatSDK.get_Event_from_SDK(self) + elif self.sdk_event_type is OlivOS.OPQBotSDK.event: + OlivOS.OPQBotSDK.get_Event_from_SDK(self) elif self.sdk_event_type is OlivOS.dingtalkSDK.event: OlivOS.dingtalkSDK.get_Event_from_SDK(self) elif self.sdk_event_type is OlivOS.biliLiveSDK.event: @@ -598,7 +602,7 @@ def funcWarpped(*args, **kwargs): callback_msg_list.append( '%s(%s)' % ( val_list_this, - warppedRes['data'][val_list_this] + str(warppedRes['data'][val_list_this]) ) ) callback_msg = ' '.join(callback_msg_list) @@ -822,6 +826,11 @@ def __send(self, send_type, target_id, message, host_id=None, flag_log=True): OlivOS.qqRedSDK.event_action.send_msg(self, 1, target_id, tmp_message, self.plugin_info['control_queue']) elif flag_type == 'group': OlivOS.qqRedSDK.event_action.send_msg(self, 2, target_id, tmp_message, self.plugin_info['control_queue']) + elif self.platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + if flag_type == 'private': + OlivOS.OPQBotSDK.event_action.send_msg(self, 'private', target_id, tmp_message, self.plugin_info['control_queue']) + elif flag_type == 'group': + OlivOS.OPQBotSDK.event_action.send_msg(self, 'group', target_id, tmp_message, self.plugin_info['control_queue']) elif self.platform['sdk'] == 'qqGuild_link': if flag_type == 'group': if hasattr(self.data, 'extend') \ @@ -1180,6 +1189,9 @@ def __set_group_leave(self, group_id, host_id, is_dismiss, flag_log=True): elif self.platform['model'] in OlivOS.flaskServerAPI.gCheckList: if host_id is None: OlivOS.onebotSDK.event_action.set_group_leave(self, group_id, is_dismiss) + elif self.platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + if host_id is None: + OlivOS.OPQBotSDK.event_action.set_group_leave(self, group_id, self.plugin_info['control_queue']) elif self.platform['sdk'] == 'telegram_poll': OlivOS.telegramSDK.event_action.set_chat_leave(self, group_id, is_dismiss) @@ -1215,6 +1227,8 @@ def __set_friend_add_request(self, flag, approve, remark, flag_log=True): OlivOS.onebotV12SDK.event_action.set_friend_add_request(self, flag, approve, remark) elif self.platform['model'] in OlivOS.flaskServerAPI.gCheckList: OlivOS.onebotSDK.event_action.set_friend_add_request(self, flag, approve, remark) + elif self.platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + OlivOS.OPQBotSDK.event_action.set_friend_add_request(self, flag, approve, self.plugin_info['control_queue']) elif self.platform['sdk'] == 'telegram_poll': pass @@ -1232,6 +1246,8 @@ def __set_group_add_request(self, flag, sub_type, approve, reason, flag_log=True OlivOS.onebotV12SDK.event_action.set_group_add_request(self, flag, sub_type, approve, reason) elif self.platform['model'] in OlivOS.flaskServerAPI.gCheckList: OlivOS.onebotSDK.event_action.set_group_add_request(self, flag, sub_type, approve, reason) + elif self.platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + OlivOS.OPQBotSDK.event_action.set_group_add_request(self, flag, sub_type, approve, self.plugin_info['control_queue']) elif self.platform['sdk'] == 'telegram_poll': pass @@ -1371,6 +1387,8 @@ def __get_group_list(self, flag_log=True): res_data = OlivOS.onebotV12SDK.event_action.get_group_list(self) elif self.platform['model'] in OlivOS.flaskServerAPI.gCheckList: res_data = OlivOS.onebotSDK.event_action.get_group_list(self) + elif self.platform['model'] in OlivOS.OPQBotLinkServerAPI.gCheckList: + res_data = OlivOS.OPQBotSDK.event_action.get_group_list(self, self.plugin_info['control_queue']) elif self.platform['sdk'] == 'telegram_poll': pass return res_data diff --git a/OlivOS/accountAPI.py b/OlivOS/core/core/accountAPI.py similarity index 85% rename from OlivOS/accountAPI.py rename to OlivOS/core/core/accountAPI.py index dfcb0f1a..fc46e33d 100644 --- a/OlivOS/accountAPI.py +++ b/OlivOS/core/core/accountAPI.py @@ -102,18 +102,28 @@ def accountFix(basic_conf_models, bot_info_dict, logger_proc): res = {} with free_port_selector() as g: # 在端口选择过程中使用上下文 for basic_conf_models_this in basic_conf_models: - if basic_conf_models[basic_conf_models_this]['type'] == 'post': - if basic_conf_models[basic_conf_models_this]['server']['auto'] == True: - basic_conf_models[basic_conf_models_this]['server']['host'] = '0.0.0.0' - if isInuse( - '127.0.0.1', - basic_conf_models[basic_conf_models_this]['server']['port'] - ): - basic_conf_models[basic_conf_models_this]['server']['port'] = g.get_free_port() + if basic_conf_models[basic_conf_models_this]['type'] == 'post' \ + and basic_conf_models[basic_conf_models_this]['server']['auto'] is True: + basic_conf_models[basic_conf_models_this]['server']['host'] = '0.0.0.0' + if isInuse( + '127.0.0.1', + basic_conf_models[basic_conf_models_this]['server']['port'] + ): + basic_conf_models[basic_conf_models_this]['server']['port'] = g.get_free_port() + if platform.system() == 'Windows' \ + and basic_conf_models[basic_conf_models_this]['type'] == 'astralqsign_lib_exe_model' \ + and basic_conf_models[basic_conf_models_this]['server']['auto'] is True: + basic_conf_models[basic_conf_models_this]['server']['host'] = '0.0.0.0' + if isInuse( + '127.0.0.1', + basic_conf_models[basic_conf_models_this]['server']['port'] + ): + basic_conf_models[basic_conf_models_this]['server']['port'] = g.get_free_port() for bot_info_dict_this in bot_info_dict: Account_data_this = bot_info_dict[bot_info_dict_this] if platform.system() == 'Windows': - if Account_data_this.platform['model'] in OlivOS.libEXEModelAPI.gCheckList: + if Account_data_this.platform['model'] in OlivOS.libEXEModelAPI.gCheckList \ + or Account_data_this.platform['model'] in OlivOS.libNapCatEXEModelAPI.gCheckList: if Account_data_this.post_info.auto == True: Account_data_this.post_info.type = 'post' Account_data_this.post_info.host = 'http://127.0.0.1' @@ -131,6 +141,11 @@ def accountFix(basic_conf_models, bot_info_dict, logger_proc): Account_data_this.post_info.host = 'ws://127.0.0.1' Account_data_this.post_info.port = g.get_free_port() Account_data_this.post_info.access_token = bot_info_dict_this + if Account_data_this.platform['model'] in OlivOS.libOPQBotEXEModelAPI.gAutoCheckList: + if Account_data_this.post_info.auto == True: + Account_data_this.post_info.type = 'websocket' + Account_data_this.post_info.host = '127.0.0.1' + Account_data_this.post_info.port = g.get_free_port() res[bot_info_dict_this] = Account_data_this return res diff --git a/OlivOS/core/core/accountMetadataAPI.py b/OlivOS/core/core/accountMetadataAPI.py new file mode 100644 index 00000000..c1d3afae --- /dev/null +++ b/OlivOS/core/core/accountMetadataAPI.py @@ -0,0 +1,335 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/accountAPI.py +@Author : MetaLeo元理 +@Contact : +@License : AGPL +@Copyright : (C) 2020-2024, OlivOS-Team +@Desc : None +''' + +import OlivOS + +accountTypeList = [ + 'QQ/NapCat/默认', + 'QQ/NapCat/9.9.11', + 'QQ/OPQ/默认', + 'QQ/GoCq/安卓平板', + 'QQ/GoCq/安卓手机', + 'KOOK', + 'KOOK/消息兼容', + 'QQ官方/公域/V2', + 'QQ官方/公域/V2/纯频道', + 'QQ官方/公域/V2/指定intents', + 'QQ官方/私域/V2', + 'QQ官方/私域/V2/指定intents', + 'QQ官方/沙盒/V2', + 'QQ官方/沙盒/V2/指定intents', + 'QQ官方/公域/V1', + 'QQ官方/私域/V1', + 'Discord', + 'Discord/指定intents', + 'Telegram', + 'Fanbook', + 'Hack.Chat', + 'Hack.Chat/私有', + 'onebotV12/正向WS', + 'onebotV11/Http', + 'onebotV11/Http/Shamrock', + 'RED协议', + 'OPQBot/正向WS', + '微信/ComWeChat', + '米游社/大别野/公域', + '米游社/大别野/私域', + '米游社/大别野/沙盒', + '渡渡语音/Dodo/V2', + '渡渡语音/Dodo/V1', + '钉钉', + 'B站直播间/游客', + 'B站直播间/登录', + 'FF14终端', + '虚拟终端', + 'QQ/OPQ/指定端口', + 'QQ/GoCq/安卓手表', + 'QQ/GoCq/默认', + 'QQ/GoCq/iPad', + 'QQ/GoCq/iMac', + 'QQ/Wq/安卓手表', + 'QQ/Wq/安卓手机', + 'QQ/Wq/安卓平板', + '接口终端', + 'QQ/NapCat/旧', + 'QQ/OPQ/指定端口/旧', + 'QQ/GoCq/旧', + 'QQ/Wq/旧', + '自定义' +] + +accountTypeMappingList = { + 'onebotV11/Http': ['qq', 'onebot', 'default', 'False', 'post'], + 'onebotV11/Http/Shamrock': ['qq', 'onebot', 'shamrock_default', 'False', 'post'], + 'onebotV11/Http/消息段': ['qq', 'onebot', 'array_default', 'False', 'post'], + 'onebotV12/正向WS': ['qq', 'onebot', 'onebotV12', 'False', 'websocket'], + 'RED协议': ['qq', 'onebot', 'red', 'False', 'websocket'], + 'OPQBot/正向WS': ['qq', 'onebot', 'opqbot_default', 'False', 'websocket'], + 'QQ/OPQ/默认': ['qq', 'onebot', 'opqbot_auto', 'True', 'websocket'], + 'QQ/OPQ/指定端口': ['qq', 'onebot', 'opqbot_port', 'True', 'websocket'], + 'QQ/OPQ/指定端口/旧': ['qq', 'onebot', 'opqbot_port_old', 'True', 'websocket'], + 'QQ/NapCat/默认': ['qq', 'onebot', 'napcat_show_new', 'True', 'post'], + 'QQ/NapCat/9.9.11': ['qq', 'onebot', 'napcat_show', 'True', 'post'], + 'QQ/NapCat/旧': ['qq', 'onebot', 'napcat_show_old', 'True', 'post'], + 'QQ/GoCq/默认': ['qq', 'onebot', 'gocqhttp_show', 'True', 'post'], + 'QQ/GoCq/安卓手机': ['qq', 'onebot', 'gocqhttp_show_Android_Phone', 'True', 'post'], + 'QQ/GoCq/安卓平板': ['qq', 'onebot', 'gocqhttp_show_Android_Pad', 'True', 'post'], + 'QQ/GoCq/安卓手表': ['qq', 'onebot', 'gocqhttp_show_Android_Watch', 'True', 'post'], + 'QQ/GoCq/iPad': ['qq', 'onebot', 'gocqhttp_show_iPad', 'True', 'post'], + 'QQ/GoCq/iMac': ['qq', 'onebot', 'gocqhttp_show_iMac', 'True', 'post'], + 'QQ/GoCq/旧': ['qq', 'onebot', 'gocqhttp_show_old', 'True', 'post'], + 'QQ/Wq/默认': ['qq', 'onebot', 'walleq_show', 'True', 'websocket'], + 'QQ/Wq/安卓手机': ['qq', 'onebot', 'walleq_show_Android_Phone', 'True', 'websocket'], + 'QQ/Wq/安卓平板': ['qq', 'onebot', 'walleq_show_Android_Pad', 'True', 'websocket'], + 'QQ/Wq/安卓手表': ['qq', 'onebot', 'walleq_show_Android_Watch', 'True', 'websocket'], + 'QQ/Wq/iPad': ['qq', 'onebot', 'walleq_show_iPad', 'True', 'websocket'], + 'QQ/Wq/iMac': ['qq', 'onebot', 'walleq_show_iMac', 'True', 'websocket'], + 'QQ/Wq/旧': ['qq', 'onebot', 'walleq_show_old', 'True', 'websocket'], + '微信/ComWeChat': ['wechat', 'onebot', 'ComWeChatBotClient', 'True', 'websocket'], + 'KOOK': ['kaiheila', 'kaiheila_link', 'default', 'True', 'websocket'], + 'KOOK/消息兼容': ['kaiheila', 'kaiheila_link', 'text', 'True', 'websocket'], + '米游社/大别野/公域': ['mhyVila', 'mhyVila_link', 'public', 'True', 'websocket'], + '米游社/大别野/私域': ['mhyVila', 'mhyVila_link', 'private', 'True', 'websocket'], + '米游社/大别野/沙盒': ['mhyVila', 'mhyVila_link', 'sandbox', 'True', 'websocket'], + 'B站直播间/游客': ['biliLive', 'biliLive_link', 'default', 'True', 'websocket'], + 'B站直播间/登录': ['biliLive', 'biliLive_link', 'login', 'True', 'websocket'], + 'QQ官方/公域/V1': ['qqGuild', 'qqGuild_link', 'public', 'True', 'websocket'], + 'QQ官方/私域/V1': ['qqGuild', 'qqGuild_link', 'private', 'True', 'websocket'], + 'QQ官方/公域/V2': ['qqGuild', 'qqGuildv2_link', 'public', 'True', 'websocket'], + 'QQ官方/公域/V2/纯频道': ['qqGuild', 'qqGuildv2_link', 'public_guild_only', 'True', 'websocket'], + 'QQ官方/公域/V2/指定intents': ['qqGuild', 'qqGuildv2_link', 'public_intents', 'True', 'websocket'], + 'QQ官方/私域/V2': ['qqGuild', 'qqGuildv2_link', 'private', 'True', 'websocket'], + 'QQ官方/私域/V2/指定intents': ['qqGuild', 'qqGuildv2_link', 'private_intents', 'True', 'websocket'], + 'QQ官方/沙盒/V2': ['qqGuild', 'qqGuildv2_link', 'sandbox', 'True', 'websocket'], + 'QQ官方/沙盒/V2/指定intents': ['qqGuild', 'qqGuildv2_link', 'sandbox_intents', 'True', 'websocket'], + 'Telegram': ['telegram', 'telegram_poll', 'default', 'True', 'post'], + 'Discord': ['discord', 'discord_link', 'default', 'True', 'websocket'], + 'Discord/指定intents': ['discord', 'discord_link', 'intents', 'True', 'websocket'], + '渡渡语音/Dodo/V2': ['dodo', 'dodo_link', 'default', 'True', 'websocket'], + '渡渡语音/Dodo/V1': ['dodo', 'dodo_link', 'v1', 'True', 'websocket'], + 'Fanbook': ['fanbook', 'fanbook_poll', 'default', 'True', 'post'], + 'Hack.Chat': ['hackChat', 'hackChat_link', 'default', 'True', 'websocket'], + 'Hack.Chat/私有': ['hackChat', 'hackChat_link', 'private', 'True', 'websocket'], + '虚拟终端': ['terminal', 'terminal_link', 'default', 'True', 'websocket'], + '接口终端': ['terminal', 'terminal_link', 'postapi', 'True', 'post'], + 'FF14终端': ['terminal', 'terminal_link', 'ff14', 'True', 'post'], + "钉钉": ["dingtalk", "dingtalk_link", "default", "True", "websocket"], + # 这个自定义屁用没有,只是占位用的 + # 对应代码里这个作为缺省项使用,不走这个逻辑 + '自定义': ['qq', 'default', 'default', 'True', 'post'] +} + +accountTypeDataList_platform = [ + 'wechat', + 'qq', + 'qqGuild', + 'kaiheila', + 'mhyVila', + 'telegram', + 'dodo', + 'fanbook', + 'discord', + 'terminal', + 'hackChat', + 'biliLive', + "dingtalk" +] + +accountTypeDataList_platform_sdk = { + 'wechat': [ + 'onebot' + ], + 'qq': [ + 'onebot' + ], + 'qqGuild': [ + 'qqGuild_link', + 'qqGuildv2_link' + ], + 'kaiheila': [ + 'kaiheila_link' + ], + 'telegram': [ + 'telegram_poll' + ], + 'dodo': [ + 'dodo_link' + # 'dodo_poll', + # 'dodobot_ea' + ], + 'mhyVila': [ + 'mhyVila_link' + ], + 'fanbook': [ + 'fanbook_poll' + ], + 'discord': [ + 'discord_link' + ], + 'terminal': [ + 'terminal_link' + ], + 'hackChat': [ + 'hackChat_link' + ], + 'biliLive': [ + 'biliLive_link' + ], + "dingtalk": [ + "dingtalk_link" + ] +} + +accountTypeDataList_platform_sdk_model = { + 'wechat': { + 'onebot': [ + 'onebotV12', + 'ComWeChatBotClient' + ] + }, + 'qq': { + 'onebot': [ + # 'gocqhttp', + # 'gocqhttp_hide', + 'default', + 'shamrock_default', + 'para_default', + 'onebotV12', + 'red', + 'gocqhttp_show', + 'gocqhttp_show_Android_Phone', + 'gocqhttp_show_Android_Pad', + 'gocqhttp_show_Android_Watch', + 'gocqhttp_show_iPad', + 'gocqhttp_show_iMac', + 'gocqhttp_show_old', + 'walleq', + 'walleq_hide', + 'walleq_show', + 'walleq_show_Android_Phone', + #'walleq_show_Android_Pad', + 'walleq_show_Android_Watch', + 'walleq_show_iPad', + 'walleq_show_iMac', + 'walleq_show_old', + 'opqbot_default', + 'opqbot_auto', + 'opqbot_port', + 'opqbot_port_old', + 'napcat', + #'napcat_hide', + 'napcat_show', + 'napcat_show_new', + 'napcat_show_old' + ] + }, + 'qqGuild': { + 'qqGuild_link': [ + 'private', + 'public', + 'default' + ], + 'qqGuildv2_link': [ + 'public', + 'public_guild_only', + 'public_intents', + 'private', + 'private_intents', + 'sandbox', + 'sandbox_intents', + 'default' + ] + }, + 'kaiheila': { + 'kaiheila_link': [ + 'default', + 'card', + 'text' + ] + }, + 'mhyVila': { + 'mhyVila_link': [ + 'private', + 'public', + 'sandbox', + 'default' + ] + }, + 'telegram': { + 'telegram_poll': [ + 'default' + ] + }, + 'discord': { + 'discord_link': [ + 'default', + 'intents' + ] + }, + 'dodo': { + 'dodo_link': [ + 'default', + 'v1', + 'v2' + ], + 'dodo_poll': [ + 'default' + ], + 'dodobot_ea': [ + 'default' + ] + }, + 'fanbook': { + 'fanbook_poll': [ + 'default', + 'private' + ] + }, + 'terminal': { + 'terminal_link': [ + 'default', + 'postapi', + 'ff14' + ] + }, + 'hackChat': { + 'hackChat_link': [ + 'default', + 'private' + ] + }, + 'biliLive': { + 'biliLive_link': [ + 'default', + 'login' + ] + }, + "dingtalk": { + "dingtalk_link": [ + "default" + ] + } +} + +accountTypeDataList_server_auto = [ + str(True), + str(False) +] + +accountTypeDataList_server_type = [ + 'post', + 'websocket' +] diff --git a/OlivOS/contentAPI.py b/OlivOS/core/core/contentAPI.py similarity index 100% rename from OlivOS/contentAPI.py rename to OlivOS/core/core/contentAPI.py diff --git a/OlivOS/diagnoseAPI.py b/OlivOS/core/core/diagnoseAPI.py similarity index 100% rename from OlivOS/diagnoseAPI.py rename to OlivOS/core/core/diagnoseAPI.py diff --git a/OlivOS/messageAPI.py b/OlivOS/core/core/messageAPI.py similarity index 98% rename from OlivOS/messageAPI.py rename to OlivOS/core/core/messageAPI.py index 598bd5b4..d7960359 100644 --- a/OlivOS/messageAPI.py +++ b/OlivOS/core/core/messageAPI.py @@ -50,7 +50,16 @@ 'walleq_show_Android_Watch': 'obv12_para', 'walleq_show_iPad': 'obv12_para', 'walleq_show_iMac': 'obv12_para', - 'walleq_show_old': 'obv12_para' + 'walleq_show_old': 'obv12_para', + 'opqbot_default': 'olivos_string', + 'opqbot_auto': 'old_string', + 'opqbot_port': 'old_string', + 'opqbot_port_old': 'old_string', + 'napcat': 'old_string', + 'napcat_hide': 'old_string', + 'napcat_show': 'old_string', + 'napcat_show_new': 'old_string', + 'napcat_show_old': 'old_string' } }, 'qqGuild': { @@ -61,8 +70,12 @@ }, 'qqGuildv2_link': { 'default': 'olivos_para', + 'sandbox': 'olivos_para', + 'sandbox_intents': 'olivos_para', 'private': 'olivos_para', - 'public': 'olivos_para' + 'private_intents': 'olivos_para', + 'public': 'olivos_para', + 'public_intents': 'olivos_para' } }, 'telegram': { diff --git a/OlivOS/metadataAPI.py b/OlivOS/core/core/metadataAPI.py similarity index 100% rename from OlivOS/metadataAPI.py rename to OlivOS/core/core/metadataAPI.py diff --git a/OlivOS/pluginAPI.py b/OlivOS/core/core/pluginAPI.py similarity index 100% rename from OlivOS/pluginAPI.py rename to OlivOS/core/core/pluginAPI.py diff --git a/OlivOS/infoAPI.py b/OlivOS/core/info/infoAPI.py similarity index 86% rename from OlivOS/infoAPI.py rename to OlivOS/core/info/infoAPI.py index e7f4dace..b72eefea 100644 --- a/OlivOS/infoAPI.py +++ b/OlivOS/core/info/infoAPI.py @@ -17,8 +17,9 @@ import OlivOS -OlivOS_Version = '0.11.25' -OlivOS_SVN = 145 +OlivOS_Version = '0.11.48' +OlivOS_SVN = 168 +OlivOS_Version_Slogan = '巧克力' # Compatible <= Plugin[compatible_svn] : Compatible # OldCompatible <= Plugin[compatible_svn] < Compatible : OldCompatible Warn @@ -28,6 +29,7 @@ OlivOS_compatible_svn_default = 0 OlivOS_Version_Short = '%s(%s)' % (OlivOS_Version, str(OlivOS_SVN)) +OlivOS_Version_Title = '%s(%s) - %s' % (OlivOS_Version, str(OlivOS_SVN), OlivOS_Version_Slogan) OlivOS_Header_UA = 'OlivOS/' + OlivOS_Version diff --git a/OlivOS/data.py b/OlivOS/core/inlineData/data.py similarity index 100% rename from OlivOS/data.py rename to OlivOS/core/inlineData/data.py diff --git a/OlivOS/updateAPI.py b/OlivOS/core/web/updateAPI.py similarity index 100% rename from OlivOS/updateAPI.py rename to OlivOS/core/web/updateAPI.py diff --git a/OlivOS/webTool.py b/OlivOS/core/web/webTool.py similarity index 100% rename from OlivOS/webTool.py rename to OlivOS/core/web/webTool.py diff --git a/OlivOS/hook.py b/OlivOS/hook.py index c7e11e7c..680bc587 100644 --- a/OlivOS/hook.py +++ b/OlivOS/hook.py @@ -22,6 +22,9 @@ # sqlite import sqlite3 +# pyjson5 +import pyjson5 + # win if platform.system() == 'Windows': import win32com.client diff --git a/OlivOS/hook_pack.py b/OlivOS/hook_pack.py index 44f1683b..b3cd03a9 100644 --- a/OlivOS/hook_pack.py +++ b/OlivOS/hook_pack.py @@ -35,6 +35,9 @@ # yaml import yaml +# pyjson5 +import pyjson5 + # openpyxl import openpyxl @@ -50,6 +53,8 @@ import prompt_toolkit import regex import rich +import smtplib +import email import sys diff --git a/OlivOS/hook_pack_debug.py b/OlivOS/hook_pack_debug.py index 5d4c21b9..35982c24 100644 --- a/OlivOS/hook_pack_debug.py +++ b/OlivOS/hook_pack_debug.py @@ -35,6 +35,9 @@ # yaml import yaml +# pyjson5 +import pyjson5 + # openpyxl import openpyxl @@ -50,5 +53,7 @@ import prompt_toolkit import regex import rich +import smtplib +import email import sys diff --git a/OlivOS/libBooter/libAstralQsignEXEModelAPI.py b/OlivOS/libBooter/libAstralQsignEXEModelAPI.py new file mode 100644 index 00000000..65700968 --- /dev/null +++ b/OlivOS/libBooter/libAstralQsignEXEModelAPI.py @@ -0,0 +1,249 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/libAstralQsignEXEModelAPI.py +@Author : MetaLeo元理 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2024, OlivOS-Team +@Desc : None +''' + +import subprocess +import time +import os +import traceback +import platform +import zipfile + +import OlivOS + +modelName = 'libAstralQsignEXEModelAPI' + +resourceUrlPath = OlivOS.infoAPI.resourceUrlPath + +gCheckList = [ + 'gocqhttp_show_Android_Phone', + 'gocqhttp_show_Android_Pad' +] + +def startAstralQsignLibExeModel( + plugin_bot_info_dict, + basic_conf_models_this, + multiprocessing_dict, + Proc_dict, + Proc_Proc_dict, + basic_conf_models, + tmp_proc_mode +): + if platform.system() == 'Windows': + flagActive = False + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['model'] in gCheckList: + flagActive = True + if flagActive: + releaseDir('./lib') + OlivOS.updateAPI.checkResouceFile( + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + resouce_api=resourceUrlPath, + resouce_name='astral-qsign', + filePath='./lib/astral-qsign.zip', + filePathUpdate='./lib/astral-qsign.zip.tmp', + filePathFORCESKIP='./lib/FORCESKIP' + ) + tmp_Proc_name = basic_conf_models_this['name'] + Proc_dict[tmp_Proc_name] = OlivOS.libAstralQsignEXEModelAPI.server( + Proc_name=tmp_Proc_name, + tx_queue=multiprocessing_dict[basic_conf_models_this['tx_queue']], + control_queue=multiprocessing_dict[basic_conf_models_this['control_queue']], + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + server_data=basic_conf_models_this['server'], + bot_info_dict=plugin_bot_info_dict, + debug_mode=False + ) + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity(tmp_proc_mode) + +class server(OlivOS.API.Proc_templet): + def __init__( + self, + Proc_name, + scan_interval=0.001, + dead_interval=1, + rx_queue=None, + tx_queue=None, + control_queue=None, + logger_proc=None, + server_data=None, + bot_info_dict=None, + debug_mode=False + ): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='astralqsign_lib_exe_model', + 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.Proc_config['debug_mode'] = debug_mode + self.Proc_data['bot_info_dict'] = {} + if type(bot_info_dict) is dict: + self.Proc_data['bot_info_dict'] = bot_info_dict + self.server_data=server_data + self.flag_run = True + + def run(self): + while self.flag_run: + self.sendLogSim( + 2, 'OlivOS libAstralQsignEXEModel server [{0}] will run under visiable mode', + [self.Proc_name] + ) + time.sleep(2) + self.setGoCqhttpModelEnableSendAll() + releaseDir("./conf") + releaseDir("./conf/astral-qsign") + unzip('./lib/astral-qsign.zip', "./conf/astral-qsign") + time.sleep(1) + tmp_env = dict(os.environ) + tmp_env['FORCE_TTY'] = '' + model_Proc = subprocess.Popen( + f".\\start.bat \"localhost\" \"{self.server_data['port']}\" \"{self.server_data['token']}\"", + cwd='.\\conf\\astral-qsign', + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + creationflags=subprocess.CREATE_NEW_CONSOLE, + env=tmp_env + ) + self.sendLog( + 2, 'OlivOS libAstralQsignEXEModel server [{0}] is running on port [{1}]', + [self.Proc_name, str(self.server_data['port'])] + ) + self.Proc_data['model_Proc'] = model_Proc + self.get_model_stdout(model_Proc) + # model_Proc.communicate(timeout = None) + self.sendLogSim( + 3, 'OlivOS libAstralQsignEXEModel server [{0}] will retry in 10s...', + [self.Proc_name] + ) + self.Proc_data['model_Proc'] = None + time.sleep(8) + + def get_model_stdout(self, model_Proc: subprocess.Popen): + for line in iter(model_Proc.stdout.readline, b''): + try: + log_data = ('%s' % line.decode('gbk', errors='replace')).rstrip('\n').rstrip('\r') + self.log(2, log_data, [('AstralQsign', 'default')]) + except Exception as e: + self.log(4, OlivOS.L10NAPI.getTrans('OlivOS libAstralQsignEXEModel failed: %s\n%s' % [ + str(e), + traceback.format_exc() + ], + modelName + )) + + def send_log_event(self, data): + self.sendControlEventSend('send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'AstralQsign', + 'event': 'log', + 'hash': self.Proc_data['bot_info_dict'].hash, + 'data': data + } + } + ) + + def setGoCqhttpModelEnableSend(self, hash): + self.sendControlEventSend('send', { + 'target': { + 'type': 'gocqhttp_lib_exe_model', + 'hash': hash + }, + 'data': { + 'action': 'skipDelay' + } + } + ) + + def sendControlEventSend(self, action, data): + if self.Proc_info.control_queue is not None: + self.Proc_info.control_queue.put( + OlivOS.API.Control.packet( + action, + data + ), + block=False + ) + + def setGoCqhttpModelEnableSendAll(self): + for bot_info_key in self.Proc_data['bot_info_dict']: + if self.Proc_data['bot_info_dict'][bot_info_key].platform['model'] in gCheckList: + self.setGoCqhttpModelEnableSend(self.Proc_data['bot_info_dict'][bot_info_key].hash) + + def sendLog(self, log_level:int, log_message:str, log_message_list:list): + self.log( + log_level, + OlivOS.L10NAPI.getTrans( + log_message, + log_message_list, + modelName + ), + [('AstralQsign', 'default')] + ) + + def sendLogSim(self, log_level:int, log_message:str, log_message_list:list): + self.log( + log_level, + OlivOS.L10NAPI.getTrans( + log_message, + log_message_list, + modelName + ), + [] + ) + + +def isBotActive(plugin_bot_info_dict:dict): + flag_need_enable = False + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['sdk'] == 'onebot' \ + and plugin_bot_info_dict[bot_info_key].platform['platform'] == 'qq' \ + and plugin_bot_info_dict[bot_info_key].platform['model'] in OlivOS.libAstralQsignEXEModelAPI.gCheckList \ + and plugin_bot_info_dict[bot_info_key].extends.get('qsign-server-protocal', None) == 'AstralQsign': + flag_need_enable = True + return flag_need_enable + +def releaseDir(dir_path): + if not os.path.exists(dir_path): + os.makedirs(dir_path) + +def support_gbk(zip_file: zipfile.ZipFile): + name_to_info = zip_file.NameToInfo + # copy map first + for name, info in name_to_info.copy().items(): + try: + real_name = name.encode('cp437').decode('gbk') + except: + real_name = name + if real_name != name: + info.filename = real_name + del name_to_info[name] + name_to_info[real_name] = info + return zip_file + +def unzip(zip_file_path, target_dir): + with support_gbk(zipfile.ZipFile(zip_file_path, 'r', zipfile.ZIP_DEFLATED)) as zip_file: + zip_file = support_gbk(zip_file) + zip_file.extractall(target_dir) diff --git a/OlivOS/libCWCBEXEModelAPI.py b/OlivOS/libBooter/libCWCBEXEModelAPI.py similarity index 100% rename from OlivOS/libCWCBEXEModelAPI.py rename to OlivOS/libBooter/libCWCBEXEModelAPI.py diff --git a/OlivOS/libEXEModelAPI.py b/OlivOS/libBooter/libEXEModelAPI.py similarity index 79% rename from OlivOS/libEXEModelAPI.py rename to OlivOS/libBooter/libEXEModelAPI.py index 1eac7eee..39183f9b 100644 --- a/OlivOS/libEXEModelAPI.py +++ b/OlivOS/libBooter/libEXEModelAPI.py @@ -27,6 +27,7 @@ import hashlib import platform import shutil +import webview import OlivOS @@ -46,92 +47,96 @@ 'gocqhttp_show_old' ] +gProtocalEXECheckList = [ + 'AstralQsign' +] + gProtocalInfo = { 'android_pad': { - '8.9.58': '''{ + 'AstralQsign': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537161402, - "sub_app_id": 537161402, + "app_id": 537220362, + "sub_app_id": 537220362, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.58.11170", - "build_time": 1684467300, + "sort_version_name": "9.0.56.16830", + "build_time": 1713424357, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2545", - "sso_version": 20, + "sdk_version": "6.0.0.2560", + "sso_version": 21, "misc_bitmap": 150470524, "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1686334718, - "qua": "V1_AND_SQ_8.9.58_4106_YYB_D", + "dump_time": 1713424357, + "qua": "V1_AND_SQ_9.0.56_6372_YYB_D", "protocol_type": 6 }''', - '8.9.63': '''{ + '9.0.56': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537164888, - "sub_app_id": 537164888, + "app_id": 537220362, + "sub_app_id": 537220362, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.63.11390", - "build_time": 1685069178, + "sort_version_name": "9.0.56.16830", + "build_time": 1713424357, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2546", - "sso_version": 20, + "sdk_version": "6.0.0.2560", + "sso_version": 21, "misc_bitmap": 150470524, "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1687796862, - "qua": "V1_AND_SQ_8.9.63_4194_YYB_D", + "dump_time": 1713424357, + "qua": "V1_AND_SQ_9.0.56_6372_YYB_D", "protocol_type": 6 }''', - '8.9.68': '''{ + '8.9.85': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537168361, - "sub_app_id": 537168361, + "app_id": 537180607, + "sub_app_id": 537180607, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.68.11565", - "build_time": 1688523354, - "apk_sign": "7772804f3cb4961f57cb764fbe4973e6", - "sdk_version": "6.0.0.2549", + "sort_version_name": "8.9.85.12820", + "build_time": 1697015435, + "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", + "sdk_version": "6.0.0.2556", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 34869472, + "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1689780543, - "qua": "V1_AND_SQ_8.9.68_4264_YYB_D", + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.85_4766_YYB_D", "protocol_type": 6 }''', - '8.9.70': '''{ + '8.9.83': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537169976, - "sub_app_id": 537169976, + "app_id": 537178685, + "sub_app_id": 537178685, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.70.11730", - "build_time": 1689956914, - "apk_sign": "e686fa90d9a33950c46de9cfb4ec7e71", - "sdk_version": "6.0.0.2551", + "sort_version_name": "8.9.83.12605", + "build_time": 1691565978, + "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", + "sdk_version": "6.0.0.2554", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 34869472, + "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1690350020, - "qua": "V1_AND_SQ_8.9.70_4330_YYB_D", + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.83_4680_YYB_D", "protocol_type": 6 }''', - '8.9.71': '''{ + '8.9.80': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537170072, - "sub_app_id": 537170072, + "app_id": 537176902, + "sub_app_id": 537176902, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.71.11735", - "build_time": 1688560152, + "sort_version_name": "8.9.80.12440", + "build_time": 1691565978, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2551", + "sdk_version": "6.0.0.2554", "sso_version": 20, "misc_bitmap": 150470524, "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1688560152, - "qua": "V1_AND_SQ_8.9.71_4332_YYB_D", - "protocol_type": 1 + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.80_4614_YYB_D", + "protocol_type": 6 }''', '8.9.73': '''{ "apk_id": "com.tencent.mobileqq", @@ -150,63 +155,78 @@ "qua": "V1_AND_SQ_8.9.73_4354_HDBM_T", "protocol_type": 6 }''', - '8.9.80': '''{ + '8.9.71': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537176902, - "sub_app_id": 537176902, + "app_id": 537170072, + "sub_app_id": 537170072, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.80.12440", - "build_time": 1691565978, + "sort_version_name": "8.9.71.11735", + "build_time": 1688560152, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2554", + "sdk_version": "6.0.0.2551", "sso_version": 20, "misc_bitmap": 150470524, "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.80_4614_YYB_D", + "dump_time": 1688560152, + "qua": "V1_AND_SQ_8.9.71_4332_YYB_D", + "protocol_type": 1 +}''', + '8.9.70': '''{ + "apk_id": "com.tencent.mobileqq", + "app_id": 537169976, + "sub_app_id": 537169976, + "app_key": "0S200MNJT807V3GE", + "sort_version_name": "8.9.70.11730", + "build_time": 1689956914, + "apk_sign": "e686fa90d9a33950c46de9cfb4ec7e71", + "sdk_version": "6.0.0.2551", + "sso_version": 20, + "misc_bitmap": 150470524, + "main_sig_map": 34869472, + "sub_sig_map": 66560, + "dump_time": 1690350020, + "qua": "V1_AND_SQ_8.9.70_4330_YYB_D", "protocol_type": 6 }''', - '8.9.83': '''{ + '8.9.68': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537178685, - "sub_app_id": 537178685, + "app_id": 537168361, + "sub_app_id": 537168361, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.83.12605", - "build_time": 1691565978, - "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2554", + "sort_version_name": "8.9.68.11565", + "build_time": 1688523354, + "apk_sign": "7772804f3cb4961f57cb764fbe4973e6", + "sdk_version": "6.0.0.2549", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 16724722, + "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.83_4680_YYB_D", + "dump_time": 1689780543, + "qua": "V1_AND_SQ_8.9.68_4264_YYB_D", "protocol_type": 6 }''', - '8.9.85': '''{ + '8.9.63': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537180607, - "sub_app_id": 537180607, + "app_id": 537164888, + "sub_app_id": 537164888, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.85.12820", - "build_time": 1697015435, + "sort_version_name": "8.9.63.11390", + "build_time": 1685069178, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2556", + "sdk_version": "6.0.0.2546", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 16724722, + "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.85_4766_YYB_D", + "dump_time": 1687796862, + "qua": "V1_AND_SQ_8.9.63_4194_YYB_D", "protocol_type": 6 -}''' - }, - 'android_phone': { +}''', '8.9.58': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537163098, - "sub_app_id": 537163098, + "app_id": 537161402, + "sub_app_id": 537161402, "app_key": "0S200MNJT807V3GE", "sort_version_name": "8.9.58.11170", "build_time": 1684467300, @@ -218,74 +238,93 @@ "sub_sig_map": 66560, "dump_time": 1686334718, "qua": "V1_AND_SQ_8.9.58_4106_YYB_D", + "protocol_type": 6 +}''' + }, + 'android_phone': { + 'AstralQsign': '''{ + "apk_id": "com.tencent.mobileqq", + "app_id": 537220362, + "sub_app_id": 537220362, + "app_key": "0S200MNJT807V3GE", + "sort_version_name": "9.0.56.16830", + "build_time": 1713424357, + "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", + "sdk_version": "6.0.0.2560", + "sso_version": 21, + "misc_bitmap": 150470524, + "main_sig_map": 34869472, + "sub_sig_map": 66560, + "dump_time": 1713424357, + "qua": "V1_AND_SQ_9.0.56_6372_YYB_D", "protocol_type": 1 }''', - '8.9.63': '''{ + '9.0.56': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537164840, - "sub_app_id": 537164840, + "app_id": 537220362, + "sub_app_id": 537220362, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.63.11390", - "build_time": 1685069178, + "sort_version_name": "9.0.56.16830", + "build_time": 1713424357, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2546", - "sso_version": 20, + "sdk_version": "6.0.0.2560", + "sso_version": 21, "misc_bitmap": 150470524, "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1687796862, - "qua": "V1_AND_SQ_8.9.63_4194_YYB_D", + "dump_time": 1713424357, + "qua": "V1_AND_SQ_9.0.56_6372_YYB_D", "protocol_type": 1 }''', - '8.9.68': '''{ + '8.9.85': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537168313, - "sub_app_id": 537168313, + "app_id": 537180568, + "sub_app_id": 537180568, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.68.11565", - "build_time": 1688523354, - "apk_sign": "7772804f3cb4961f57cb764fbe4973e6", - "sdk_version": "6.0.0.2549", + "sort_version_name": "8.9.85.12820", + "build_time": 1697015435, + "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", + "sdk_version": "6.0.0.2556", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 34869472, + "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1689780543, - "qua": "V1_AND_SQ_8.9.68_4264_YYB_D", + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.85_4766_YYB_D", "protocol_type": 1 }''', - '8.9.70': '''{ + '8.9.83': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537169928, - "sub_app_id": 537169928, + "app_id": 537178646, + "sub_app_id": 537178646, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.70.11730", - "build_time": 1689956914, - "apk_sign": "e686fa90d9a33950c46de9cfb4ec7e71", - "sdk_version": "6.0.0.2551", + "sort_version_name": "8.9.83.12605", + "build_time": 1691565978, + "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", + "sdk_version": "6.0.0.2554", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 34869472, + "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1690350020, - "qua": "V1_AND_SQ_8.9.70_4330_YYB_D", + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.83_4680_YYB_D", "protocol_type": 1 }''', - '8.9.71': '''{ + '8.9.80': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537170024, - "sub_app_id": 537170024, + "app_id": 537176863, + "sub_app_id": 537176863, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.71.11735", - "build_time": 1688560152, + "sort_version_name": "8.9.80.12440", + "build_time": 1691565978, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2551", + "sdk_version": "6.0.0.2554", "sso_version": 20, "misc_bitmap": 150470524, "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1688560152, - "qua": "V1_AND_SQ_8.9.71_4332_YYB_D", + "dump_time": 1692110632, + "qua": "V1_AND_SQ_8.9.80_4614_YYB_D", "protocol_type": 1 }''', '8.9.73': '''{ @@ -305,55 +344,89 @@ "qua": "V1_AND_SQ_8.9.73_4354_HDBM_T", "protocol_type": 1 }''', - '8.9.80': '''{ + '8.9.71': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537176863, - "sub_app_id": 537176863, + "app_id": 537170024, + "sub_app_id": 537170024, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.80.12440", - "build_time": 1691565978, + "sort_version_name": "8.9.71.11735", + "build_time": 1688560152, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2554", + "sdk_version": "6.0.0.2551", "sso_version": 20, "misc_bitmap": 150470524, "main_sig_map": 16724722, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.80_4614_YYB_D", + "dump_time": 1688560152, + "qua": "V1_AND_SQ_8.9.71_4332_YYB_D", "protocol_type": 1 }''', - '8.9.83': '''{ + '8.9.70': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537178646, - "sub_app_id": 537178646, + "app_id": 537169928, + "sub_app_id": 537169928, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.83.12605", - "build_time": 1691565978, + "sort_version_name": "8.9.70.11730", + "build_time": 1689956914, + "apk_sign": "e686fa90d9a33950c46de9cfb4ec7e71", + "sdk_version": "6.0.0.2551", + "sso_version": 20, + "misc_bitmap": 150470524, + "main_sig_map": 34869472, + "sub_sig_map": 66560, + "dump_time": 1690350020, + "qua": "V1_AND_SQ_8.9.70_4330_YYB_D", + "protocol_type": 1 +}''', + '8.9.68': '''{ + "apk_id": "com.tencent.mobileqq", + "app_id": 537168313, + "sub_app_id": 537168313, + "app_key": "0S200MNJT807V3GE", + "sort_version_name": "8.9.68.11565", + "build_time": 1688523354, + "apk_sign": "7772804f3cb4961f57cb764fbe4973e6", + "sdk_version": "6.0.0.2549", + "sso_version": 20, + "misc_bitmap": 150470524, + "main_sig_map": 34869472, + "sub_sig_map": 66560, + "dump_time": 1689780543, + "qua": "V1_AND_SQ_8.9.68_4264_YYB_D", + "protocol_type": 1 +}''', + '8.9.63': '''{ + "apk_id": "com.tencent.mobileqq", + "app_id": 537164840, + "sub_app_id": 537164840, + "app_key": "0S200MNJT807V3GE", + "sort_version_name": "8.9.63.11390", + "build_time": 1685069178, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2554", + "sdk_version": "6.0.0.2546", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 16724722, + "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.83_4680_YYB_D", + "dump_time": 1687796862, + "qua": "V1_AND_SQ_8.9.63_4194_YYB_D", "protocol_type": 1 }''', - '8.9.85': '''{ + '8.9.58': '''{ "apk_id": "com.tencent.mobileqq", - "app_id": 537180568, - "sub_app_id": 537180568, + "app_id": 537163098, + "sub_app_id": 537163098, "app_key": "0S200MNJT807V3GE", - "sort_version_name": "8.9.85.12820", - "build_time": 1697015435, + "sort_version_name": "8.9.58.11170", + "build_time": 1684467300, "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2556", + "sdk_version": "6.0.0.2545", "sso_version": 20, "misc_bitmap": 150470524, - "main_sig_map": 16724722, + "main_sig_map": 34869472, "sub_sig_map": 66560, - "dump_time": 1692110632, - "qua": "V1_AND_SQ_8.9.85_4766_YYB_D", + "dump_time": 1686334718, + "qua": "V1_AND_SQ_8.9.58_4106_YYB_D", "protocol_type": 1 }''' } @@ -398,14 +471,27 @@ def startGoCqhttpLibExeModel( logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], bot_info_dict=plugin_bot_info_dict[bot_info_key], target_proc=basic_conf_models[basic_conf_models_this['target_proc']], + sub_target_proc=basic_conf_models.get(basic_conf_models_this.get('sub_target_proc', None), None), debug_mode=False ) Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity(tmp_proc_mode) class server(OlivOS.API.Proc_templet): - def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=None, tx_queue=None, - control_queue=None, logger_proc=None, target_proc=None, debug_mode=False, bot_info_dict=None): + def __init__( + self, + Proc_name, + scan_interval=0.001, + dead_interval=1, + rx_queue=None, + tx_queue=None, + control_queue=None, + logger_proc=None, + target_proc=None, + sub_target_proc=None, + debug_mode=False, + bot_info_dict=None + ): OlivOS.API.Proc_templet.__init__( self, Proc_name=Proc_name, @@ -420,6 +506,7 @@ def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=Non self.Proc_config['debug_mode'] = debug_mode self.Proc_data['bot_info_dict'] = bot_info_dict self.Proc_config['target_proc'] = target_proc + self.Proc_config['sub_target_proc'] = sub_target_proc self.Proc_data['check_qrcode_flag'] = False self.Proc_data['check_stdin'] = False self.Proc_data['model_Proc'] = None @@ -448,11 +535,16 @@ def run(self): with open('./conf/gocqhttp/filter.json', 'w+') as tmp: tmp.write('{}') releaseDir('./conf/gocqhttp/' + self.Proc_data['bot_info_dict'].hash) - goTypeConfig(self.Proc_data['bot_info_dict'], self.Proc_config['target_proc']).setConfig() - if False and (self.Proc_data['bot_info_dict'].platform['model'] in [ - 'gocqhttp', - 'gocqhttp_hide' - ]): + goTypeConfig(self.Proc_data['bot_info_dict'], self.Proc_config['target_proc'], self.Proc_config['sub_target_proc']).setConfig() + if 'qsign-server-protocal' in self.Proc_data['bot_info_dict'].extends \ + and type(self.Proc_data['bot_info_dict'].extends['qsign-server-protocal']) is str \ + and self.Proc_data['bot_info_dict'].extends['qsign-server-protocal'] in gProtocalEXECheckList: + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS libEXEModel server [{0}] will run in {1}s...', + [self.Proc_name, str(8)], modelName + )) + self.check_protocalEXE(8, 0.25) + if False and (self.Proc_data['bot_info_dict'].platform['model'] in ['gocqhttp', 'gocqhttp_hide']): model_Proc = subprocess.Popen( '..\\..\\..\\lib\\go-cqhttp.exe faststart', cwd='.\\conf\\gocqhttp\\' + self.Proc_data['bot_info_dict'].hash, @@ -638,6 +730,25 @@ def send_QRCode_event(self, path: str): } ) + def check_protocalEXE(self, delayS:int, interval:int): + flag_skip = False + for i in range(int(delayS / interval)): + if self.Proc_info.rx_queue.empty(): + time.sleep(interval) + else: + try: + rx_packet_data = self.Proc_info.rx_queue.get(block=False) + if type(rx_packet_data.key) is dict \ + and 'data' in rx_packet_data.key \ + and 'action' in rx_packet_data.key['data'] \ + and 'skipDelay' == rx_packet_data.key['data']['action']: + flag_skip = True + break + except: + rx_packet_data = None + if flag_skip: + time.sleep(2) + def send_log_event(self, data): self.sendControlEventSend('send', { 'target': { @@ -664,13 +775,15 @@ def sendControlEventSend(self, action, data): class goTypeConfig(object): - def __init__(self, bot_info_dict, target_proc): + def __init__(self, bot_info_dict, target_proc, sub_target_proc): self.bot_info_dict = bot_info_dict self.target_proc = target_proc + self.sub_target_proc = sub_target_proc self.config_file_str = '' self.config_file_format = {} def setConfig(self): + global gProtocalEXECheckList self.config_file_str = ''' account: # 账号相关 uin: {uin} # QQ账号 @@ -726,7 +839,7 @@ def setConfig(self): # 为 false 时,将不会自动注册实例,在签名服务器重启或实例被销毁后需要重启 go-cqhttp 以获取实例 # 否则后续消息将不会正常签名。关闭此项后可以考虑开启签名服务器端 auto_register 避免需要重启 # 由于实现问题,当前建议关闭此项,推荐开启签名服务器的自动注册实例 - auto-register: false + auto-register: {auto-register} # 是否在 token 过期后立即自动刷新签名 token(在需要签名时才会检测到,主要防止 token 意外丢失) # 独立于定时刷新 auto-refresh-token: false @@ -857,6 +970,7 @@ def setConfig(self): key: '-' authorization: '-' ''' + self.config_file_format['auto-register'] = 'false' if 'qsign-server' in self.bot_info_dict.extends \ and type(self.bot_info_dict.extends['qsign-server']) is list \ and len(self.bot_info_dict.extends['qsign-server']) > 0: @@ -868,7 +982,18 @@ def setConfig(self): self.config_file_format['sign-servers-data'] += ''' - url: '%s' key: '%s' - authorization: '-' ''' % (tmp_data_this['addr'], tmp_data_this['key']) + authorization: '-' +''' % (tmp_data_this['addr'], tmp_data_this['key']) + + if 'qsign-server-protocal' in self.bot_info_dict.extends \ + and type(self.bot_info_dict.extends['qsign-server-protocal']) is str \ + and self.bot_info_dict.extends['qsign-server-protocal'] in gProtocalEXECheckList: + self.config_file_format['sign-servers-data'] = ''' + - url: 'http://localhost:%s/' + key: '%s' + authorization: '-' +''' % (str(self.sub_target_proc['server']['port']), str(self.sub_target_proc['server']['token'])) + self.config_file_format['auto-register'] = 'true' self.config_file_str = self.config_file_str.format(**self.config_file_format) @@ -932,17 +1057,21 @@ def accountFix(bot_info_dict, logger_proc): if 'qsign-server-protocal' in bot_info_dict[bot_hash].extends \ and type(bot_info_dict[bot_hash].extends['qsign-server-protocal']) is str: if bot_info_dict[bot_hash].platform['model'] in [ - 'gocqhttp_show_Android_Pad' - ]: - if bot_info_dict[bot_hash].extends['qsign-server-protocal'] in gProtocalInfo['android_pad']: - protocal_info = gProtocalInfo['android_pad'][bot_info_dict[bot_hash].extends['qsign-server-protocal']] - protocal_num = 6 - if bot_info_dict[bot_hash].platform['model'] in [ + 'gocqhttp_show_Android_Pad', 'gocqhttp_show_Android_Phone' ]: - if bot_info_dict[bot_hash].extends['qsign-server-protocal'] in gProtocalInfo['android_phone']: - protocal_info = gProtocalInfo['android_phone'][bot_info_dict[bot_hash].extends['qsign-server-protocal']] - protocal_num = 1 + if bot_info_dict[bot_hash].platform['model'] in [ + 'gocqhttp_show_Android_Pad', + ]: + if bot_info_dict[bot_hash].extends['qsign-server-protocal'] in gProtocalInfo['android_pad']: + protocal_info = gProtocalInfo['android_pad'][bot_info_dict[bot_hash].extends['qsign-server-protocal']] + protocal_num = 6 + elif bot_info_dict[bot_hash].platform['model'] in [ + 'gocqhttp_show_Android_Phone' + ]: + if bot_info_dict[bot_hash].extends['qsign-server-protocal'] in gProtocalInfo['android_phone']: + protocal_info = gProtocalInfo['android_phone'][bot_info_dict[bot_hash].extends['qsign-server-protocal']] + protocal_num = 1 if protocal_info is not None: file_path = './conf/gocqhttp/' + bot_hash + '/data/versions/%d.json' % protocal_num try: @@ -1047,3 +1176,143 @@ def getRandomStringRange(length:int, string:str): def releaseDir(dir_path): if not os.path.exists(dir_path): os.makedirs(dir_path) + + +def setGoCqhttpTokenSend( + control_queue, + hash, + token +): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet('send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'gocqhttp', + 'event': 'token_get', + 'token': token, + 'hash': hash, + } + } + ), + block=False + ) + +def sendOpentxTuringTestPage( + control_queue, + name:str, + title:str, + url:str +): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet( + 'init_type_open_tx_turingTest_webview_page', + { + 'target': { + 'action': 'init', + 'name': name + }, + 'data': { + 'title': title, + 'url': url + } + } + ), + block=False + ) + +def sendStopTxTuringTestPage(control_queue, Proc_name): + if control_queue is not None: + control_queue.put( + OlivOS.API.Control.packet('stop', Proc_name), + block=False + ) + +def txTuringTest_evaluate_js(window): + window.evaluate_js( + r""" + mqq.invoke = function(a,b,c){return pywebview.api.invoke(b, JSON.stringify(c))}; + """ + ) + +class txTuringTestApi(object): + def __init__(self, Proc_name, hash, control_queue): + self.Proc_name = Proc_name + self.hash = hash + self.control_queue = control_queue + + def invoke(self, b, c): + data = json.loads(c) + if type(data) is dict \ + and 'ticket' in data \ + and type(data['ticket']) is str: + setGoCqhttpTokenSend( + self.control_queue, + self.hash, + data['ticket'] + ) + sendStopTxTuringTestPage( + self.control_queue, + self.Proc_name + ) + +class txTuringTestPage(OlivOS.API.Proc_templet): + def __init__( + self, + Proc_name='tx_turingTest_webview_page', + scan_interval=0.001, + dead_interval=1, + rx_queue=None, + tx_queue=None, + logger_proc=None, + control_queue=None, + title='OlivOS Turing Test Page', + url=None + ): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='tx_turingTest_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, + 'control_queue': control_queue + } + + def run(self): + releaseDir('./data') + releaseDir('./data/webview') + releaseDir('./data/webview/%s' % self.Proc_name) + if self.UIData['url'] != None: + txTuringTestApi_obj = txTuringTestApi( + self.Proc_name, + self.Proc_name.split('=')[-1], + self.UIData['control_queue'], + ) + window = webview.create_window( + title=self.UIData['title'], + url=self.UIData['url'], + background_color='#00A0EA', + js_api=txTuringTestApi_obj + ) + webview.start( + txTuringTest_evaluate_js, + window, + private_mode=False, + storage_path='./data/webview/%s' % self.Proc_name, + gui='edgechromium' + ) + + # 发送并等待结束 + sendStopTxTuringTestPage(self.Proc_info.control_queue, self.Proc_name) diff --git a/OlivOS/libBooter/libNapCatEXEModelAPI.py b/OlivOS/libBooter/libNapCatEXEModelAPI.py new file mode 100644 index 00000000..05533086 --- /dev/null +++ b/OlivOS/libBooter/libNapCatEXEModelAPI.py @@ -0,0 +1,416 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/libNapCatEXEModelAPI.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import multiprocessing +import subprocess +import threading +import time +import os +import traceback +import json +import copy +import random +import uuid +import hashlib +import platform +import shutil +import zipfile + +import OlivOS + +modelName = 'libNapCatEXEModelAPI' + +resourceUrlPath = OlivOS.infoAPI.resourceUrlPath + +gCheckList = [ + 'napcat', + 'napcat_hide', + 'napcat_show', + 'napcat_show_new', + 'napcat_show_old' +] + + +def startNapCatLibExeModel( + plugin_bot_info_dict, + basic_conf_models_this, + multiprocessing_dict, + Proc_dict, + Proc_Proc_dict, + basic_conf_models, + tmp_proc_mode +): + if platform.system() == 'Windows': + flagActive = False + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['model'] in gCheckList: + flagActive = True + if flagActive: + releaseDir('./lib') + OlivOS.updateAPI.checkResouceFile( + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + resouce_api=resourceUrlPath, + resouce_name='NapCat-QQ-Win-9.9.11-24568', + filePath='./lib/NapCat.zip', + filePathUpdate='./lib/NapCat.zip.tmp', + filePathFORCESKIP='./lib/FORCESKIP' + ) + OlivOS.updateAPI.checkResouceFile( + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + resouce_api=resourceUrlPath, + resouce_name='NapCat-QQ-Win-9.9.12-26000', + filePath='./lib/NapCatNew.zip', + filePathUpdate='./lib/NapCat.zip.tmp', + filePathFORCESKIP='./lib/FORCESKIP' + ) + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['model'] in gCheckList: + tmp_Proc_name = basic_conf_models_this['name'] + '=' + bot_info_key + tmp_queue_name = basic_conf_models_this['rx_queue'] + '=' + bot_info_key + multiprocessing_dict[tmp_queue_name] = multiprocessing.Queue() + Proc_dict[tmp_Proc_name] = OlivOS.libNapCatEXEModelAPI.server( + Proc_name=tmp_Proc_name, + scan_interval=basic_conf_models_this['interval'], + dead_interval=basic_conf_models_this['dead_interval'], + rx_queue=multiprocessing_dict[tmp_queue_name], + tx_queue=multiprocessing_dict[basic_conf_models_this['tx_queue']], + control_queue=multiprocessing_dict[basic_conf_models_this['control_queue']], + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + bot_info_dict=plugin_bot_info_dict[bot_info_key], + target_proc=basic_conf_models[basic_conf_models_this['target_proc']], + debug_mode=False + ) + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity(tmp_proc_mode) + +class server(OlivOS.API.Proc_templet): + def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=None, tx_queue=None, + control_queue=None, logger_proc=None, target_proc=None, debug_mode=False, bot_info_dict=None): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='napcat_lib_exe_model', + 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.Proc_config['debug_mode'] = debug_mode + self.Proc_data['bot_info_dict'] = bot_info_dict + 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): + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'napcat', + 'napcat_show' + ]: + self.send_init_event() + while self.flag_run: + releaseDir('./lib') + if not os.path.exists('./lib/NapCat.zip'): + self.log(3, OlivOS.L10NAPI.getTrans( + 'OlivOS libNapCatEXEModel server [{0}] can`t found target lib', + [self.Proc_name], modelName + )) + break + releaseDir('./conf') + releaseDir('./conf/napcat') + releaseDir(f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}") + releaseDir(f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}/config") + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'napcat_show_new' + ]: + unzip('./lib/NapCatNew.zip', f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}") + else: + unzip('./lib/NapCat.zip', f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}") + napcatTypeConfig(self.Proc_data['bot_info_dict'], self.Proc_config['target_proc']).setConfig() + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'napcat', + 'napcat_show_new', + 'napcat_show' + ]: + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS libNapCatEXEModel server [{0}] will run under visiable mode', + [self.Proc_name], modelName + )) + self.clear_napcat() + self.Proc_data['check_qrcode_flag'] = False + self.Proc_data['check_stdin'] = False + time.sleep(2) + self.Proc_data['check_qrcode_flag'] = True + self.Proc_data['check_stdin'] = True + threading.Thread( + target=self.check_qrcode, + args=(), + daemon=self.deamon + ).start() + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'napcat_show_new' + ]: + tmp_env = dict(os.environ) + subprocess.call( + 'start powershell .\\BootWay05.ps1', + shell=True, + cwd='.\\conf\\napcat\\' + self.Proc_data['bot_info_dict'].hash, + env=tmp_env + ) + self.flag_run = False + else: + tmp_env = dict(os.environ) + model_Proc = subprocess.Popen( + f".\\napcat-utf8.bat -q {self.Proc_data['bot_info_dict'].id}", + cwd=f".\\conf\\napcat\\{self.Proc_data['bot_info_dict'].hash}", + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + creationflags=subprocess.CREATE_NEW_CONSOLE, + env=tmp_env + ) + self.Proc_data['model_Proc'] = model_Proc + threading.Thread( + target=self.check_stdin, + args=(model_Proc,), + daemon=self.deamon + ).start() + self.get_model_stdout(model_Proc) + # model_Proc.communicate(timeout = None) + self.log(3, OlivOS.L10NAPI.getTrans( + 'OlivOS libNapCatEXEModel 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 [ + 'napcat_show_old' + ]: + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS libNapCatEXEModel server [{0}] will run under visiable mode', + [self.Proc_name], modelName + )) + tmp_env = dict(os.environ) + subprocess.call( + 'start cmd /K "title NapCat For OlivOS| .\\napcat-utf8.bat -q ' + str(self.Proc_data['bot_info_dict'].id) + '"', + shell=True, + cwd='.\\conf\\napcat\\' + self.Proc_data['bot_info_dict'].hash, + env=tmp_env + ) + 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'] + if self.Proc_data['bot_info_dict'].id is not None: + tmp_self_data = '%s|%s' % ( + self.Proc_data['bot_info_dict'].platform['platform'], + str(self.Proc_data['bot_info_dict'].id) + ) + return tmp_self_data + + def check_stdin(self, model_Proc: subprocess.Popen): + while self.Proc_data['check_stdin']: + if self.Proc_info.rx_queue.empty(): + time.sleep(0.02) + else: + try: + rx_packet_data = self.Proc_info.rx_queue.get(block=False) + except: + rx_packet_data = None + if 'data' in rx_packet_data.key and 'action' in rx_packet_data.key['data']: + if 'input' == rx_packet_data.key['data']['action']: + if 'data' in rx_packet_data.key['data']: + input_raw = str(rx_packet_data.key['data']['data']) + input_data = ('%s\r\n' % input_raw).encode('utf-8') + model_Proc.stdin.write(input_data) + model_Proc.stdin.flush() + log_data = ('%s' % input_raw) + self.send_log_event(log_data) + self.log(2, log_data, [ + (self.getBotIDStr(), 'default'), + ('napcat', 'default'), + ('onebot_send', 'default') + ]) + + def get_model_stdout(self, model_Proc: subprocess.Popen): + for line in iter(model_Proc.stdout.readline, b''): + try: + log_data = ('%s' % line.decode('utf-8', errors='replace')).rstrip('\n') + self.send_log_event(log_data) + self.log(1, log_data, [ + (self.getBotIDStr(), 'default'), + ('napcat', 'default'), + ('onebot', 'default') + ]) + except Exception as e: + self.log(4, OlivOS.L10NAPI.getTrans('OlivOS libNapCatEXEModel failed: %s\n%s' % [ + str(e), + traceback.format_exc() + ], + modelName + )) + + def send_init_event(self): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'napcat', + 'event': 'init', + 'hash': self.Proc_data['bot_info_dict'].hash + } + } + ) + + def clear_napcat(self): + file_path = f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}/qrcode.png" + if os.path.exists(file_path): + os.remove(file_path) + + def check_qrcode(self): + count = 2 * 60 + file_path = f"./conf/napcat/{self.Proc_data['bot_info_dict'].hash}/qrcode.png" + while count > 0 and self.Proc_data['check_qrcode_flag']: + if os.path.exists(file_path): + self.send_QRCode_event(file_path) + count = 0 + count -= 1 + time.sleep(1) + self.Proc_data['check_qrcode_flag'] = False + + def send_QRCode_event(self, path: str): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'napcat', + 'event': 'qrcode', + 'hash': self.Proc_data['bot_info_dict'].hash, + 'path': path + } + } + ) + + def send_log_event(self, data): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'napcat', + 'event': 'log', + 'hash': self.Proc_data['bot_info_dict'].hash, + 'data': data + } + } + ) + + def sendControlEventSend(self, action, data): + if self.Proc_info.control_queue is not None: + self.Proc_info.control_queue.put( + OlivOS.API.Control.packet( + action, + data + ), + block=False + ) + + +class napcatTypeConfig(object): + def __init__(self, bot_info_dict:OlivOS.API.bot_info_T, target_proc): + self.bot_info_dict = bot_info_dict + self.target_proc = target_proc + self.config_file_str = '' + self.config_file_data = '' + self.config_file_format = {} + + def setConfig(self): + self.config_file_format['uin'] = str(self.bot_info_dict.id) + 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['postUrls'] = f"http://127.0.0.1:{self.target_proc['server']['port']}/OlivOSMsgApi/qq/onebot/default" + + self.config_file_data = { + "http": { + "enable": True, + "host": "", + "port": self.config_file_format['port'], + "secret": self.config_file_format['token'], + "enableHeart": False, + "enablePost": True, + "postUrls": [ + self.config_file_format['postUrls'] + ] + }, + "ws": { + "enable": False, + "host": "", + "port": 3001 + }, + "reverseWs": { + "enable": False, + "urls": [] + }, + "debug": False, + "heartInterval": 30000, + "messagePostFormat": "array", + "enableLocalFile2Url": True, + "musicSignUrl": "", + "reportSelfMessage": False, + "token": "" + } + + self.config_file_str = json.dumps(self.config_file_data, ensure_ascii = False, indent = 4) + + with open(f'./conf/napcat/{self.bot_info_dict.hash}/config/onebot11_{self.bot_info_dict.id}.json', 'w+', encoding='utf-8') as tmp: + tmp.write(self.config_file_str) + +def releaseDir(dir_path): + if not os.path.exists(dir_path): + os.makedirs(dir_path) + +def support_gbk(zip_file: zipfile.ZipFile): + name_to_info = zip_file.NameToInfo + # copy map first + for name, info in name_to_info.copy().items(): + try: + real_name = name.encode('cp437').decode('gbk') + except: + real_name = name + if real_name != name: + info.filename = real_name + del name_to_info[name] + name_to_info[real_name] = info + return zip_file + +def unzip(zip_file_path, target_dir): + with support_gbk(zipfile.ZipFile(zip_file_path, 'r', zipfile.ZIP_DEFLATED)) as zip_file: + zip_file = support_gbk(zip_file) + zip_file.extractall(target_dir) diff --git a/OlivOS/libBooter/libOPQBotEXEModelAPI.py b/OlivOS/libBooter/libOPQBotEXEModelAPI.py new file mode 100644 index 00000000..4e22fbbc --- /dev/null +++ b/OlivOS/libBooter/libOPQBotEXEModelAPI.py @@ -0,0 +1,371 @@ +# -*- encoding: utf-8 -*- +''' +_______________________ ________________ +__ __ \__ /____ _/_ | / /_ __ \_ ___/ +_ / / /_ / __ / __ | / /_ / / /____ \ +/ /_/ /_ /____/ / __ |/ / / /_/ /____/ / +\____/ /_____/___/ _____/ \____/ /____/ + +@File : OlivOS/libOPQBotEXEModelAPI.py +@Author : lunzhiPenxil仑质 +@Contact : lunzhipenxil@gmail.com +@License : AGPL +@Copyright : (C) 2020-2023, OlivOS-Team +@Desc : None +''' + +import multiprocessing +import subprocess +import threading +import time +import os +import traceback +import json +import copy +import random +import uuid +import hashlib +import re +import platform +import shutil + +import OlivOS + +modelName = 'libOPQBotEXEModelAPI' + +resourceUrlPath = OlivOS.infoAPI.resourceUrlPath + +gCheckList = [ + 'opqbot_auto', + 'opqbot_port', + 'opqbot_port_old' +] + +gAutoCheckList = [ + 'opqbot_auto' +] + + +def startOPQBotLibExeModel( + plugin_bot_info_dict, + basic_conf_models_this, + multiprocessing_dict, + Proc_dict, + Proc_Proc_dict, + basic_conf_models, + tmp_proc_mode +): + if platform.system() == 'Windows': + flagActive = False + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['model'] in gCheckList: + flagActive = True + if flagActive: + releaseDir('./lib') + OlivOS.updateAPI.checkResouceFile( + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + resouce_api=resourceUrlPath, + resouce_name='OPQBot', + filePath='./lib/OPQBot.exe', + filePathUpdate='./lib/OPQBot.exe.tmp', + filePathFORCESKIP='./lib/FORCESKIP' + ) + for bot_info_key in plugin_bot_info_dict: + if plugin_bot_info_dict[bot_info_key].platform['model'] in gCheckList: + tmp_Proc_name = basic_conf_models_this['name'] + '=' + bot_info_key + tmp_queue_name = basic_conf_models_this['rx_queue'] + '=' + bot_info_key + multiprocessing_dict[tmp_queue_name] = multiprocessing.Queue() + Proc_dict[tmp_Proc_name] = OlivOS.libOPQBotEXEModelAPI.server( + Proc_name=tmp_Proc_name, + scan_interval=basic_conf_models_this['interval'], + dead_interval=basic_conf_models_this['dead_interval'], + rx_queue=multiprocessing_dict[tmp_queue_name], + tx_queue=multiprocessing_dict[basic_conf_models_this['tx_queue']], + control_queue=multiprocessing_dict[basic_conf_models_this['control_queue']], + logger_proc=Proc_dict[basic_conf_models_this['logger_proc']], + bot_info_dict=plugin_bot_info_dict[bot_info_key], + target_proc=None, + debug_mode=False + ) + Proc_Proc_dict[tmp_Proc_name] = Proc_dict[tmp_Proc_name].start_unity(tmp_proc_mode) + +class server(OlivOS.API.Proc_templet): + def __init__(self, Proc_name, scan_interval=0.001, dead_interval=1, rx_queue=None, tx_queue=None, + control_queue=None, logger_proc=None, target_proc=None, debug_mode=False, bot_info_dict=None): + OlivOS.API.Proc_templet.__init__( + self, + Proc_name=Proc_name, + Proc_type='opqbot_lib_exe_model', + 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.Proc_config['debug_mode'] = debug_mode + self.Proc_data['bot_info_dict'] = bot_info_dict + 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.Proc_data['qrcode_flag'] = True + self.Proc_data['stdout_start_ts'] = None + self.flag_run = True + + def run(self): + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'opqbot_auto', + 'opqbot_port', + 'opqbot_port_old' + ]: + self.send_init_event() + while self.flag_run: + releaseDir('./lib') + if not os.path.exists('./lib/OPQBot.exe'): + self.log(3, OlivOS.L10NAPI.getTrans( + 'OlivOS libOPQBotEXEModel server [{0}] can`t found target lib', + [self.Proc_name], modelName + )) + break + releaseDir('./conf') + releaseDir('./conf/OPQBot') + releaseDir('./conf/OPQBot/' + self.Proc_data['bot_info_dict'].hash) + copyFile( + src='./lib/OPQBot.exe', + dst='./conf/OPQBot/' + self.Proc_data['bot_info_dict'].hash + '/OPQBot.exe' + ) + if self.Proc_data['bot_info_dict'].platform['model'] in [ + 'opqbot_auto', + 'opqbot_port', + ]: + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS libOPQBotEXEModel server [{0}] will run under visiable mode', + [self.Proc_name], modelName + )) + self.clear_OPQBot() + self.Proc_data['check_qrcode_flag'] = False + self.Proc_data['check_stdin'] = False + time.sleep(2) + self.Proc_data['check_qrcode_flag'] = True + self.Proc_data['check_stdin'] = True + threading.Thread( + target=self.check_qrcode, + args=(), + daemon=self.deamon + ).start() + tmp_env = dict(os.environ) + model_Proc = subprocess.Popen( + f'.\\OPQBot.exe' + f' -port {self.Proc_data["bot_info_dict"].post_info.port}' + f' -token {self.Proc_data["bot_info_dict"].post_info.access_token}', + cwd='.\\conf\\OPQBot\\' + self.Proc_data['bot_info_dict'].hash, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + creationflags=subprocess.CREATE_NEW_CONSOLE, + env=tmp_env + ) + self.Proc_data['model_Proc'] = model_Proc + threading.Thread( + target=self.check_stdin, + args=(model_Proc,), + daemon=self.deamon + ).start() + threading.Thread( + target=self.check_model_flag, + args=(), + daemon=self.deamon + ).start() + self.get_model_stdout(model_Proc) + # model_Proc.communicate(timeout = None) + self.log(3, OlivOS.L10NAPI.getTrans( + 'OlivOS libOPQBotEXEModel 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 [ + 'opqbot_port_old' + ]: + self.log(2, OlivOS.L10NAPI.getTrans( + 'OlivOS libOPQBotEXEModel server [{0}] will run under visiable mode', + [self.Proc_name], modelName + )) + tmp_env = dict(os.environ) + subprocess.call( + 'start cmd /K "title OPQBot For OlivOS|.\\OPQBot.exe -port %s -token %s"' % ( + str(self.Proc_data['bot_info_dict'].post_info.port), + str(self.Proc_data['bot_info_dict'].post_info.access_token) + ), + shell=True, + cwd='.\\conf\\OPQBot\\' + self.Proc_data['bot_info_dict'].hash, + env=tmp_env + ) + self.flag_run = False + pass + + 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: + tmp_self_data = '%s|%s' % ( + self.Proc_data['bot_info_dict'].platform['platform'], + str(self.Proc_data['bot_info_dict'].id) + ) + return tmp_self_data + + def check_stdin(self, model_Proc: subprocess.Popen): + while self.Proc_data['check_stdin']: + if self.Proc_info.rx_queue.empty(): + time.sleep(0.02) + else: + try: + rx_packet_data = self.Proc_info.rx_queue.get(block=False) + except: + rx_packet_data = None + if 'data' in rx_packet_data.key and 'action' in rx_packet_data.key['data']: + if 'input' == rx_packet_data.key['data']['action']: + if 'data' in rx_packet_data.key['data']: + input_raw = str(rx_packet_data.key['data']['data']) + input_data = ('%s\r\n' % input_raw).encode('utf-8') + model_Proc.stdin.write(input_data) + model_Proc.stdin.flush() + log_data = ('%s' % input_raw) + self.send_log_event(log_data) + self.log(2, log_data, [ + (self.getBotIDStr(), 'default'), + ('OPQBot', 'default'), + ('stdin_send', 'default') + ]) + + def get_model_stdout(self, model_Proc: subprocess.Popen): + for line in iter(model_Proc.stdout.readline, b''): + try: + log_data = ('%s' % line.decode('utf-8', errors='replace')).rstrip('\n') + self.send_log_event(log_data) + self.log(1, log_data, [ + (self.getBotIDStr(), 'default'), + ('OPQBot', 'default'), + ('onebot', 'default') + ]) + self.check_model_stdout(log_data) + except Exception as e: + self.log(4, OlivOS.L10NAPI.getTrans('OlivOS libOPQBotEXEModel failed: %s\n%s' % [ + str(e), + traceback.format_exc() + ], + modelName + )) + + def check_model_stdout(self, line_data:str): + if self.Proc_data['qrcode_flag']: + try: + matchRes = re.match( + r'^\d{4}\/\d{2}\/\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}\s\[C\]\s{2}User\s\d+\s登录成功,即将刷新相关数据$', + line_data + ) + if matchRes != None: + self.Proc_data['qrcode_flag'] = False + except: + pass + + def check_model_flag(self): + self.Proc_data['stdout_start_ts'] = time.time() + while time.time() - self.Proc_data['stdout_start_ts'] < 5: + if not self.Proc_data['qrcode_flag']: + break + time.sleep(0.2) + #print("self.Proc_data['qrcode_flag'] = " + str(self.Proc_data['qrcode_flag'])) + if self.Proc_data['qrcode_flag']: + self.send_QRCode_event( + f'http://' + f'{self.Proc_data["bot_info_dict"].post_info.host}:' + f'{self.Proc_data["bot_info_dict"].post_info.port}/v1/login/getqrcode' + ) + + def send_init_event(self): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'opqbot', + 'event': 'init', + 'hash': self.Proc_data['bot_info_dict'].hash + } + } + ) + + def clear_OPQBot(self): + file_path = './conf/OPQBot/' + self.Proc_data['bot_info_dict'].hash + '/qrcode.png' + if os.path.exists(file_path): + os.remove(file_path) + + def check_qrcode(self): + count = 2 * 60 + file_path = './conf/OPQBot/' + self.Proc_data['bot_info_dict'].hash + '/qrcode.png' + while count > 0 and self.Proc_data['check_qrcode_flag']: + if os.path.exists(file_path): + self.send_QRCode_event(file_path) + count = 0 + count -= 1 + time.sleep(1) + self.Proc_data['check_qrcode_flag'] = False + + def send_QRCode_event(self, path: str): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'opqbot', + 'event': 'qrcode', + 'hash': self.Proc_data['bot_info_dict'].hash, + 'path': path + } + } + ) + + def send_log_event(self, data): + self.sendControlEventSend( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'opqbot', + 'event': 'log', + 'hash': self.Proc_data['bot_info_dict'].hash, + 'data': data + } + } + ) + + def sendControlEventSend(self, action, data): + if self.Proc_info.control_queue is not None: + self.Proc_info.control_queue.put( + OlivOS.API.Control.packet( + action, + data + ), + block=False + ) + +def releaseDir(dir_path): + if not os.path.exists(dir_path): + os.makedirs(dir_path) + +def copyFile(src, dst): + try: + shutil.copyfile(src = src, dst = dst) + except: + pass diff --git a/OlivOS/libWQEXEModelAPI.py b/OlivOS/libBooter/libWQEXEModelAPI.py similarity index 100% rename from OlivOS/libWQEXEModelAPI.py rename to OlivOS/libBooter/libWQEXEModelAPI.py diff --git a/OlivOS/multiLoginUIAPI.py b/OlivOS/nativeGUI/multiLoginUIAPI.py similarity index 66% rename from OlivOS/multiLoginUIAPI.py rename to OlivOS/nativeGUI/multiLoginUIAPI.py index 2e5f0c96..6b8c600a 100644 --- a/OlivOS/multiLoginUIAPI.py +++ b/OlivOS/nativeGUI/multiLoginUIAPI.py @@ -23,6 +23,7 @@ import platform import traceback import json +import copy from tkinter import ttk from tkinter import messagebox @@ -139,7 +140,7 @@ def start(self): self.UIObject['root'] = tkinter.Tk() else: self.UIObject['root'] = tkinter.Toplevel() - self.UIObject['root'].title('OlivOS 登录管理器') + self.UIObject['root'].title('OlivOS 登录管理器 - %s' % OlivOS.infoAPI.OlivOS_Version_Title) self.UIObject['root'].geometry('518x400') self.UIObject['root'].resizable( width=False, @@ -149,17 +150,22 @@ def start(self): self.UIObject['tree'] = ttk.Treeview(self.UIObject['root']) self.UIObject['tree']['show'] = 'headings' - self.UIObject['tree']['columns'] = ('ID', 'PLATFORM', 'SDK', 'MODEL') - self.UIObject['tree'].column('ID', width=100) - self.UIObject['tree'].column('PLATFORM', width=100) - self.UIObject['tree'].column('SDK', width=100) - self.UIObject['tree'].column('MODEL', width=100) + #self.UIObject['tree']['columns'] = ('ID', 'PLATFORM', 'SDK', 'MODEL') + self.UIObject['tree']['columns'] = ('ID', 'TYPE') + self.UIObject['tree'].column('ID', width=200) + self.UIObject['tree'].column('TYPE', width=200) + #self.UIObject['tree'].column('PLATFORM', width=100) + #self.UIObject['tree'].column('SDK', width=100) + #self.UIObject['tree'].column('MODEL', width=100) self.UIObject['tree'].heading('ID', text='ID') - self.UIObject['tree'].heading('PLATFORM', text='PLATFORM') - self.UIObject['tree'].heading('SDK', text='SDK') - self.UIObject['tree'].heading('MODEL', text='MODEL') + self.UIObject['tree'].heading('TYPE', text='账号类型') + #self.UIObject['tree'].heading('PLATFORM', text='PLATFORM') + #self.UIObject['tree'].heading('SDK', text='SDK') + #self.UIObject['tree'].heading('MODEL', text='MODEL') self.UIObject['tree']['selectmode'] = 'browse' - self.tree_load() + # 这个加载流程现在需要后置 + # 因为它现在需要同时控制 self.UIObject['root_frame_first_root'] 这个遮罩层是否显示 + #self.tree_load() self.UIObject['tree'].place(x=0, y=0, width=500, height=350) self.UIObject['tree_rightkey_menu'] = tkinter.Menu(self.UIObject['root'], tearoff=False) self.UIObject['root'].bind('', lambda x: self.tree_rightKey(x)) @@ -211,7 +217,7 @@ def start(self): self.tree_UI_Button_init( name='root_Button_COMMIT', - text='确认', + text='启动 OlivOS', command=lambda: self.account_data_commit(), x=391, y=358, @@ -219,12 +225,169 @@ def start(self): height=34 ) + # 无账号时的引导遮罩层 + self.UIObject['root_frame_first_root'] = tkinter.Frame(self.UIObject['root']) + self.UIObject['root_frame_first_root'].configure(relief = tkinter.FLAT) + self.UIObject['root_frame_first_root'].configure(bg = self.UIConfig['color_001'], borderwidth = 0) + # 这个组件使用这两个方法进行显示和隐藏 + # 位置信息依赖此处的数据 + # 是否显示通常受到数据控制其是否显示 + # self.frame_show('root_frame_first_root') + # self.frame_hide('root_frame_first_root') + self.UIConfig['root_frame_first_root_place'] = { + 'x': 0, + 'y': 0, + 'width': 518, + 'height': 400 + } + + self.UIObject['root_frame_first_root_label_note_new'] = tkinter.Label( + self.UIObject['root_frame_first_root'], + text = '\n'.join( + [ + '欢迎使用 OlivOS', + '', + '你可以在这里创建你的第一个账号' + ] + ) + ) + self.UIObject['root_frame_first_root_label_note_new'].configure( + bg = self.UIConfig['color_001'], + fg = self.UIConfig['color_004'], + font = ('等线', 16, 'bold') + ) + self.UIObject['root_frame_first_root_label_note_new'].place( + x=int(518/2 - 518/2), + y=int(400/2/2 + 20 - 200/2), + width=518, + height=200 + ) + + self.tree_UI_Button_init( + name='root_frame_first_root_Button_FIRST_NEW', + text='创建一个账号', + command=lambda: self.tree_edit('create'), + x=int(518/2 - 250/2), + y=int(400/2 + 20 - 48/2), + width=250, + height=48, + root='root_frame_first_root' + ) + self.UIObject['root_frame_first_root_Button_FIRST_NEW'].configure(font='等线 16 bold') + + self.UIObject['root_frame_first_root_label_note_commit'] = tkinter.Label( + self.UIObject['root_frame_first_root'], + text = '\n'.join( + [ + '或者你也可以' + ] + ) + ) + self.UIObject['root_frame_first_root_label_note_commit'].configure( + bg = self.UIConfig['color_001'], + fg = self.UIConfig['color_004'], + font = ('等线', 12, 'bold') + ) + self.UIObject['root_frame_first_root_label_note_commit'].place( + x=391 - 16 * 6 - 8, + y=358, + width=16 * 6, + height=34 + ) + + self.tree_UI_Button_init( + name='root_frame_first_root_Button_FIRST_COMMIT', + text='直接启动', + command=lambda: self.frame_show('root_frame_skip_root'), + x=391, + y=358, + width=117, + height=34, + root='root_frame_first_root' + ) + + # 确认真的要无账号启动时的引导遮罩层 + self.UIObject['root_frame_skip_root'] = tkinter.Frame(self.UIObject['root']) + self.UIObject['root_frame_skip_root'].configure(relief = tkinter.FLAT) + self.UIObject['root_frame_skip_root'].configure(bg = self.UIConfig['color_001'], borderwidth = 0) + # 这个组件使用这两个方法进行显示和隐藏 + # 位置信息依赖此处的数据 + # 需要按钮流程控制其是否显示 + # self.frame_show('root_frame_skip_root') + # self.frame_hide('root_frame_skip_root') + self.UIConfig['root_frame_skip_root_place'] = { + 'x': 0, + 'y': 0, + 'width': 518, + 'height': 400 + } + + self.UIObject['root_frame_skip_root_label_note_commit'] = tkinter.Label( + self.UIObject['root_frame_skip_root'], + text = '\n'.join( + [ + 'OlivOS 的大部分功能都是基于账号进行的', + '', + '无账号的确可以正常运行', + '但请确保你真的明白你要做什么', + '', + '你真的要这么做吗?' + ] + ) + ) + self.UIObject['root_frame_skip_root_label_note_commit'].configure( + bg = self.UIConfig['color_001'], + fg = self.UIConfig['color_004'], + font = ('等线', 16, 'bold') + ) + self.UIObject['root_frame_skip_root_label_note_commit'].place( + x=int(518/2 - 518/2), + y=int(400/2/2 - 200/2), + width=518, + height=200 + ) + + self.tree_UI_Button_init( + name='root_frame_skip_root_Button_FIRST_COMMIT', + text='是的,我要直接启动', + command=lambda: self.account_data_commit(), + x=int(518/2 - 250/2), + y=int(400/2 + 20 - 48/2), + width=250, + height=48, + root='root_frame_skip_root' + ) + self.UIObject['root_frame_skip_root_Button_FIRST_COMMIT'].configure(font='等线 16 bold') + + self.tree_UI_Button_init( + name='root_frame_skip_root_Button_FIRST_COMMIT_BACK', + text='我点错了,让我回去', + command=lambda: self.frame_hide('root_frame_skip_root'), + x=int(518/2 - 250/2), + y=int(400/2 + 20 + 48 * 1 + 15 - 48/2), + width=250, + height=48, + root='root_frame_skip_root' + ) + self.UIObject['root_frame_skip_root_Button_FIRST_COMMIT_BACK'].configure(font='等线 16 bold') + + # 这个数据加载过程会在后续多次反复执行 + self.tree_load() + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') self.UIObject['root'].mainloop() return self.res + def frame_show(self, name): + if name in ['root_frame_first_root', 'root_frame_skip_root']: + self.UIObject[name].place(**self.UIConfig[f'{name}_place']) + + def frame_hide(self, name): + if name in ['root_frame_first_root', 'root_frame_skip_root']: + self.UIObject[name].place_forget() + def buttom_action(self, name, action): if name in self.UIObject: if action == '': @@ -232,9 +395,9 @@ def buttom_action(self, name, action): if action == '': self.UIObject[name].configure(bg=self.UIConfig['color_003']) - def tree_UI_Button_init(self, name, text, command, x, y, width, height): + def tree_UI_Button_init(self, name, text, command, x, y, width, height, root='root'): self.UIObject[name] = tkinter.Button( - self.UIObject['root'], + self.UIObject[root], text=text, command=command, bd=0, @@ -242,7 +405,8 @@ def tree_UI_Button_init(self, name, text, command, x, y, width, height): activeforeground=self.UIConfig['color_001'], bg=self.UIConfig['color_003'], fg=self.UIConfig['color_004'], - relief='groove' + relief='groove', + font='等线 12 bold' ) self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) @@ -272,11 +436,16 @@ def tree_load(self): text=Account_hash_this, values=( self.UIData['Account_data'][Account_hash_this].id, - self.UIData['Account_data'][Account_hash_this].platform['platform'], - self.UIData['Account_data'][Account_hash_this].platform['sdk'], - self.UIData['Account_data'][Account_hash_this].platform['model'] + self.get_account_data_type_name(Account_hash_this) + #self.UIData['Account_data'][Account_hash_this].platform['platform'], + #self.UIData['Account_data'][Account_hash_this].platform['sdk'], + #self.UIData['Account_data'][Account_hash_this].platform['model'] ) ) + if len(self.UIData['Account_data']) <= 0: + self.frame_show('root_frame_first_root') + else: + self.frame_hide('root_frame_first_root') def tree_edit(self, action): hash_key_how = None @@ -313,6 +482,27 @@ def account_data_commit(self): sendAccountUpdate(self, self.control_queue, self.UIData['Account_data']) self.UIObject['root'].destroy() + def get_account_data_type_name(self, hash_key): + res = '自定义' + list_data_check = [ + str(self.UIData['Account_data'][hash_key].platform['platform']), + str(self.UIData['Account_data'][hash_key].platform['sdk']), + str(self.UIData['Account_data'][hash_key].platform['model']), + str(self.UIData['Account_data'][hash_key].post_info.auto), + str(self.UIData['Account_data'][hash_key].post_info.type) + ] + for type_this in OlivOS.accountMetadataAPI.accountTypeList: + flag_hit = True + for list_data_check_i in range(len(list_data_check)): + if list_data_check[list_data_check_i] \ + != OlivOS.accountMetadataAPI.accountTypeMappingList[type_this][list_data_check_i]: + flag_hit = False + break + if flag_hit: + break + if flag_hit: + res = type_this + return res class TreeEditUI(object): def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=None): @@ -340,16 +530,21 @@ def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=Non 'edit_root_Entry_Extend2_StringVar': tkinter.StringVar(), 'edit_root_Combobox_qsign_protocal_StringVar': tkinter.StringVar(), 'edit_root_Combobox_qsign_protocal_list': [ + 'AstralQsign', '手动', - '8.9.58', - '8.9.63', - '8.9.68', - '8.9.70', - '8.9.71', - '8.9.73', - '8.9.80', + '9.0.56', + '8.9.85', '8.9.83', - '8.9.85' + '8.9.80', + '8.9.73', + '8.9.71', + '8.9.70', + '8.9.68', + '8.9.63', + '8.9.58', + ], + 'edit_root_Combobox_qsign_protocal_list_exemod': [ + 'AstralQsign' ], 'edit_root_Entry_Extend_list': [ 'edit_root_Entry_Extend', @@ -358,48 +553,7 @@ def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=Non 'edit_root_Entry_qsign_list': [], 'edit_root_Entry_qsign_num': 1, 'edit_root_Combobox_dict': { - 'type_list': [ - 'KOOK', - 'KOOK/消息兼容', - '钉钉', - '渡渡语音/Dodo/V2', - '渡渡语音/Dodo/V1', - 'QQ官方/公域/V2', - 'QQ官方/私域/V2', - 'QQ官方/公域/V1', - 'QQ官方/私域/V1', - '米游社/大别野/公域', - '米游社/大别野/私域', - '米游社/大别野/沙盒', - 'Discord', - 'Telegram', - 'Fanbook', - 'Hack.Chat', - 'Hack.Chat/私有', - 'onebotV12/正向WS', - 'onebotV11/Http', - 'RED协议', - 'B站直播间/游客', - 'B站直播间/登录', - 'FF14终端', - '虚拟终端', - '接口终端', - 'QQ/GoCq/安卓手表', - 'QQ/GoCq/安卓手机', - 'QQ/GoCq/安卓平板', - 'QQ/GoCq/默认', - 'QQ/GoCq/iPad', - 'QQ/GoCq/iMac', - 'QQ/Wq/安卓手表', - 'QQ/Wq/安卓手机', - 'QQ/Wq/安卓平板', - '微信/ComWeChat', - 'QQ/GoCq/旧', - 'QQ/Wq/旧', - '自定义' - ], - # 各类账号组合的匹配与注册表 - # type: [platform, sdk, model, server_auto, server_type, {data_dict}] + 'type_list': OlivOS.accountMetadataAPI.accountTypeList, 'type_note_list': { 'QQ/GoCq/安卓手表': '密码留空即尝试使用扫码登录', 'QQ/GoCq/旧': '密码留空即尝试使用扫码登录', @@ -407,7 +561,13 @@ def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=Non 'QQ/Wq/旧': '密码留空即尝试使用扫码登录', '微信/ComWeChat': '启动后需要再运行特定版本微信', 'Hack.Chat': '密码可以留空', - 'RED协议': 'HTTP可以不填,反正也没实现' + 'RED协议': 'HTTP可以不填,反正也没实现', + 'QQ/OPQ/默认': '简单对接OPQ,使用闭源框架有账号安全风险,OlivOS不对此负责', + 'QQ/OPQ/指定端口': '简单对接OPQ,使用闭源框架有账号安全风险,OlivOS不对此负责', + 'QQ/OPQ/指定端口/旧': '简单对接OPQ,使用闭源框架有账号安全风险,OlivOS不对此负责', + 'QQ/NapCat/默认': '需要已经安装不低于9.9.12版本QQ', + 'QQ/NapCat/9.9.11': '需要已经安装不高于9.9.11版本QQ', + 'QQ/NapCat/旧': '使用本方法需要已经安装较新版本QQ' }, 'type_clear_note_list': { 'QQ/GoCq/默认': './conf/gocqhttp/{bothash}', @@ -420,7 +580,13 @@ def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=Non 'QQ/Wq/安卓手表': './conf/walleq/{bothash}', 'QQ/Wq/安卓手机': './conf/walleq/{bothash}', 'QQ/Wq/安卓平板': './conf/walleq/{bothash}', - 'QQ/Wq/旧': './conf/walleq/{bothash}' + 'QQ/Wq/旧': './conf/walleq/{bothash}', + 'QQ/OPQ/默认': './conf/OPQBot/{bothash}', + 'QQ/OPQ/指定端口': './conf/OPQBot/{bothash}', + 'QQ/OPQ/指定端口/旧': './conf/OPQBot/{bothash}', + 'QQ/NapCat/默认': './conf/napcat/{bothash}', + 'QQ/NapCat/9.9.11': './conf/napcat/{bothash}', + 'QQ/NapCat/旧': './conf/napcat/{bothash}' }, 'type_extend_note_list': { #'QQ/GoCq/默认': ['签名服务器', 'sign-server'], @@ -455,413 +621,274 @@ def __init__(self, action, Account_data, hash_key=None, edit_commit_callback=Non 'QQ/GoCq/安卓平板': {'地址': 'sign-server', 'KEY': 'key'}, 'QQ/GoCq/旧': {'地址': 'sign-server', 'KEY': 'key'} }, - 'type_mapping_list': { - 'onebotV11/Http': ['qq', 'onebot', 'default', 'False', 'post', { - '账号': 'edit_root_Entry_ID', - '地址': 'edit_root_Entry_Server_host', - '端口': 'edit_root_Entry_Server_port', - 'TOKEN': 'edit_root_Entry_Server_access_token', - } - ], - 'onebotV12/正向WS': ['qq', 'onebot', 'onebotV12', 'False', 'websocket', { - '账号': 'edit_root_Entry_ID', - '地址': 'edit_root_Entry_Server_host', - '端口': 'edit_root_Entry_Server_port', - 'TOKEN': 'edit_root_Entry_Server_access_token', - } - ], - 'RED协议': ['qq', 'onebot', 'red', 'False', 'websocket', { - '账号': 'edit_root_Entry_ID', - 'WS地址': 'edit_root_Entry_Server_host', - 'WS端口': 'edit_root_Entry_Server_port', - 'TOKEN': 'edit_root_Entry_Server_access_token', - } - ], - 'QQ/GoCq/默认': ['qq', 'onebot', 'gocqhttp_show', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/安卓手机': ['qq', 'onebot', 'gocqhttp_show_Android_Phone', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/安卓平板': ['qq', 'onebot', 'gocqhttp_show_Android_Pad', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/安卓手表': ['qq', 'onebot', 'gocqhttp_show_Android_Watch', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/iPad': ['qq', 'onebot', 'gocqhttp_show_iPad', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/iMac': ['qq', 'onebot', 'gocqhttp_show_iMac', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/GoCq/旧': ['qq', 'onebot', 'gocqhttp_show_old', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/默认': ['qq', 'onebot', 'walleq_show', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/安卓手机': ['qq', 'onebot', 'walleq_show_Android_Phone', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/安卓平板': ['qq', 'onebot', 'walleq_show_Android_Pad', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/安卓手表': ['qq', 'onebot', 'walleq_show_Android_Watch', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/iPad': ['qq', 'onebot', 'walleq_show_iPad', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/iMac': ['qq', 'onebot', 'walleq_show_iMac', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - 'QQ/Wq/旧': ['qq', 'onebot', 'walleq_show_old', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID', - '密码': 'edit_root_Entry_Password', - # 推荐使用扫码登录时,可以隐藏密码栏 - } - ], - '微信/ComWeChat': ['wechat', 'onebot', 'ComWeChatBotClient', 'True', 'websocket', { - '微信号': 'edit_root_Entry_ID' - } - ], - 'KOOK': ['kaiheila', 'kaiheila_link', 'default', 'True', 'websocket', { - 'Token': 'edit_root_Entry_Server_access_token' - } - ], - 'KOOK/消息兼容': ['kaiheila', 'kaiheila_link', 'text', 'True', 'websocket', { - 'Token': 'edit_root_Entry_Server_access_token' - } - ], - '米游社/大别野/公域': ['mhyVila', 'mhyVila_link', 'public', 'True', 'websocket', { - 'Bot_Id': 'edit_root_Entry_ID', - 'Secret': 'edit_root_Entry_Password', - 'Pub_Key': 'edit_root_Entry_Server_access_token' - } - ], - '米游社/大别野/私域': ['mhyVila', 'mhyVila_link', 'private', 'True', 'websocket', { - 'Bot_Id': 'edit_root_Entry_ID', - 'Secret': 'edit_root_Entry_Password', - 'Pub_Key': 'edit_root_Entry_Server_access_token' - } - ], - '米游社/大别野/沙盒': ['mhyVila', 'mhyVila_link', 'sandbox', 'True', 'websocket', { - 'Bot_Id': 'edit_root_Entry_ID', - 'Secret': 'edit_root_Entry_Password', - 'Pub_Key': 'edit_root_Entry_Server_access_token', - '别野号': 'edit_root_Entry_Server_port' - } - ], - 'B站直播间/游客': ['biliLive', 'biliLive_link', 'default', 'True', 'websocket', { - '直播间ID': 'edit_root_Entry_Server_access_token' - } - ], - 'B站直播间/登录': ['biliLive', 'biliLive_link', 'login', 'True', 'websocket', { - '直播间ID': 'edit_root_Entry_Server_access_token' - } - ], - 'QQ官方/公域/V1': ['qqGuild', 'qqGuild_link', 'public', 'True', 'websocket', { - 'AppID': 'edit_root_Entry_ID', - '机器人令牌': 'edit_root_Entry_Server_access_token' - } - ], - 'QQ官方/私域/V1': ['qqGuild', 'qqGuild_link', 'private', 'True', 'websocket', { - 'AppID': 'edit_root_Entry_ID', - '机器人令牌': 'edit_root_Entry_Server_access_token' - } - ], - 'QQ官方/公域/V2': ['qqGuild', 'qqGuildv2_link', 'public', 'True', 'websocket', { - 'AppID': 'edit_root_Entry_ID', - 'AppSecret': 'edit_root_Entry_Server_access_token' - } - ], - 'QQ官方/私域/V2': ['qqGuild', 'qqGuildv2_link', 'private', 'True', 'websocket', { - 'AppID': 'edit_root_Entry_ID', - 'AppSecret': 'edit_root_Entry_Server_access_token' - } - ], - 'Telegram': ['telegram', 'telegram_poll', 'default', 'True', 'post', { - 'TOKEN': 'edit_root_Entry_Server_access_token' - } - ], - 'Discord': ['discord', 'discord_link', 'default', 'True', 'websocket', { - 'TOKEN': 'edit_root_Entry_Server_access_token' - } - ], - '渡渡语音/Dodo/V2': ['dodo', 'dodo_link', 'default', 'True', 'websocket', { - 'BotID': 'edit_root_Entry_ID', - 'Bot私钥': 'edit_root_Entry_Server_access_token' - } - ], - '渡渡语音/Dodo/V1': ['dodo', 'dodo_link', 'v1', 'True', 'websocket', { - 'BotID': 'edit_root_Entry_ID', - 'Bot私钥': 'edit_root_Entry_Server_access_token' - } - ], - 'Fanbook': ['fanbook', 'fanbook_poll', 'default', 'True', 'post', { - 'Token': 'edit_root_Entry_Server_access_token' - } - ], - 'Hack.Chat': ['hackChat', 'hackChat_link', 'default', 'True', 'websocket', { - '房间名称': 'edit_root_Entry_Server_host', - 'Bot名称': 'edit_root_Entry_Server_access_token', - '密码': 'edit_root_Entry_Password' - } - ], - 'Hack.Chat/私有': ['hackChat', 'hackChat_link', 'private', 'True', 'websocket', { - '房间名称': 'edit_root_Entry_Server_host', - 'Bot名称': 'edit_root_Entry_Server_access_token', - '密码': 'edit_root_Entry_Password' - } - ], - '虚拟终端': ['terminal', 'terminal_link', 'default', 'True', 'websocket', { - '账号': 'edit_root_Entry_ID' - } - ], - '接口终端': ['terminal', 'terminal_link', 'postapi', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '端口': 'edit_root_Entry_Server_port' - } - ], - 'FF14终端': ['terminal', 'terminal_link', 'ff14', 'True', 'post', { - '账号': 'edit_root_Entry_ID', - '端口': 'edit_root_Entry_Server_port', - '回调端口': 'edit_root_Entry_Server_access_token' - } - ], - "钉钉": ["dingtalk", "dingtalk_link", "default", "True", "websocket", { - "Robot Code": 'edit_root_Entry_ID', - # "" - } - ], - '自定义': ['qq', 'default', 'default', 'True', 'post', { - 'ID': 'edit_root_Entry_ID', - 'PASSWORD': 'edit_root_Entry_Password', - 'HOST': 'edit_root_Entry_Server_host', - 'PORT': 'edit_root_Entry_Server_port', - 'TOKEN': 'edit_root_Entry_Server_access_token' - } - ], - }, - 'platform_list': [ - 'wechat', - 'qq', - 'qqGuild', - 'kaiheila', - 'mhyVila', - 'telegram', - 'dodo', - 'fanbook', - 'discord', - 'terminal', - 'hackChat', - 'biliLive', - "dingtalk" - ], - 'platform_sdk_list': { - 'wechat': [ - 'onebot' - ], - 'qq': [ - 'onebot' - ], - 'qqGuild': [ - 'qqGuild_link', - 'qqGuildv2_link' - ], - 'kaiheila': [ - 'kaiheila_link' - ], - 'telegram': [ - 'telegram_poll' - ], - 'dodo': [ - 'dodo_link' - # 'dodo_poll', - # 'dodobot_ea' - ], - 'mhyVila': [ - 'mhyVila_link' - ], - 'fanbook': [ - 'fanbook_poll' - ], - 'discord': [ - 'discord_link' - ], - 'terminal': [ - 'terminal_link' - ], - 'hackChat': [ - 'hackChat_link' - ], - 'biliLive': [ - 'biliLive_link' - ], - "dingtalk": [ - "dingtalk_link" - ] - }, - 'platform_sdk_model_list': { - 'wechat': { - 'onebot': [ - 'onebotV12', - 'ComWeChatBotClient' - ] + # 各类账号组合的匹配与注册表 + # 原本为合并格式,并在此处维护 + # type: [platform, sdk, model, server_auto, server_type, {data_dict}] + # 现拆分为两个表,使用时合并,以便于维护 + # type: [platform, sdk, model, server_auto, server_type] + [{data_dict}] + # 前半位于 OlivOS.accountMetadataAPI + # 后半位于此处 + 'type_mapping_list': {}, + 'type_mapping_list_Entry_slot': { + 'onebotV11/Http': { + '账号': 'edit_root_Entry_ID', + '地址': 'edit_root_Entry_Server_host', + '端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'qq': { - 'onebot': [ - # 'gocqhttp', - # 'gocqhttp_hide', - 'default', - 'onebotV12', - 'red', - 'gocqhttp_show', - 'gocqhttp_show_Android_Phone', - 'gocqhttp_show_Android_Pad', - 'gocqhttp_show_Android_Watch', - 'gocqhttp_show_iPad', - 'gocqhttp_show_iMac', - 'gocqhttp_show_old', - 'walleq', - 'walleq_hide', - 'walleq_show', - 'walleq_show_Android_Phone', - #'walleq_show_Android_Pad', - 'walleq_show_Android_Watch', - 'walleq_show_iPad', - 'walleq_show_iMac', - 'walleq_show_old' - ] + 'onebotV11/Http/Shamrock': { + '账号': 'edit_root_Entry_ID', + '地址': 'edit_root_Entry_Server_host', + '端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'qqGuild': { - 'qqGuild_link': [ - 'private', - 'public', - 'default' - ], - 'qqGuildv2_link': [ - 'private', - 'public', - 'default' - ] + 'onebotV11/Http/消息段': { + '账号': 'edit_root_Entry_ID', + '地址': 'edit_root_Entry_Server_host', + '端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'kaiheila': { - 'kaiheila_link': [ - 'default', - 'card', - 'text' - ] + 'onebotV12/正向WS': { + '账号': 'edit_root_Entry_ID', + '地址': 'edit_root_Entry_Server_host', + '端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'mhyVila': { - 'mhyVila_link': [ - 'private', - 'public', - 'sandbox', - 'default' - ] + 'RED协议': { + '账号': 'edit_root_Entry_ID', + 'WS地址': 'edit_root_Entry_Server_host', + 'WS端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'telegram': { - 'telegram_poll': [ - 'default' - ] + 'OPQBot/正向WS': { + 'QQ号': 'edit_root_Entry_ID', + '服务地址': 'edit_root_Entry_Server_host', + '服务端口': 'edit_root_Entry_Server_port', }, - 'discord': { - 'discord_link': [ - 'default' - ] + 'QQ/OPQ/默认': { + 'QQ号': 'edit_root_Entry_ID', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'dodo': { - 'dodo_link': [ - 'default', - 'v1', - 'v2' - ], - 'dodo_poll': [ - 'default' - ], - 'dodobot_ea': [ - 'default' - ] + 'QQ/OPQ/指定端口': { + 'QQ号': 'edit_root_Entry_ID', + '服务端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'fanbook': { - 'fanbook_poll': [ - 'default', - 'private' - ] + 'QQ/OPQ/指定端口/旧': { + 'QQ号': 'edit_root_Entry_ID', + '服务端口': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token', }, - 'terminal': { - 'terminal_link': [ - 'default', - 'postapi', - 'ff14' - ] + 'QQ/NapCat/默认': { + 'QQ号': 'edit_root_Entry_ID', }, - 'hackChat': { - 'hackChat_link': [ - 'default', - 'private' - ] + 'QQ/NapCat/9.9.11': { + 'QQ号': 'edit_root_Entry_ID', }, - 'biliLive': { - 'biliLive_link': [ - 'default', - 'login' - ] + 'QQ/NapCat/旧': { + 'QQ号': 'edit_root_Entry_ID', + 'TOKEN': 'edit_root_Entry_Server_access_token', + '服务端口': 'edit_root_Entry_Server_port', }, - "dingtalk": { - "dingtalk_link": [ - "default" - ] - } - } - }, 'edit_root_Combobox_Server_auto_list': [ - 'True', - 'False' - ], 'edit_root_Combobox_Server_type_list': [ - 'post', - 'websocket' - ]} + 'QQ/GoCq/默认': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/安卓手机': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/安卓平板': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/安卓手表': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/iPad': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/iMac': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/GoCq/旧': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/默认': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/安卓手机': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/安卓平板': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/安卓手表': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/iPad': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/iMac': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + 'QQ/Wq/旧': { + '账号': 'edit_root_Entry_ID', + '密码': 'edit_root_Entry_Password', + }, + '微信/ComWeChat': { + '微信号': 'edit_root_Entry_ID' + }, + 'KOOK': { + 'Token': 'edit_root_Entry_Server_access_token' + }, + 'KOOK/消息兼容': { + 'Token': 'edit_root_Entry_Server_access_token' + }, + '米游社/大别野/公域': { + 'Bot_Id': 'edit_root_Entry_ID', + 'Secret': 'edit_root_Entry_Password', + 'Pub_Key': 'edit_root_Entry_Server_access_token' + }, + '米游社/大别野/私域': { + 'Bot_Id': 'edit_root_Entry_ID', + 'Secret': 'edit_root_Entry_Password', + 'Pub_Key': 'edit_root_Entry_Server_access_token' + }, + '米游社/大别野/沙盒': { + 'Bot_Id': 'edit_root_Entry_ID', + 'Secret': 'edit_root_Entry_Password', + 'Pub_Key': 'edit_root_Entry_Server_access_token', + '别野号': 'edit_root_Entry_Server_port' + }, + 'B站直播间/游客': { + '直播间ID': 'edit_root_Entry_Server_access_token' + }, + 'B站直播间/登录': { + '直播间ID': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/公域/V1': { + 'AppID': 'edit_root_Entry_ID', + '机器人令牌': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/私域/V1': { + 'AppID': 'edit_root_Entry_ID', + '机器人令牌': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/公域/V2': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/公域/V2/纯频道': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/公域/V2/指定intents': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token', + 'intents': 'edit_root_Entry_Server_port' + }, + 'QQ官方/私域/V2': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/私域/V2/指定intents': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token', + 'intents': 'edit_root_Entry_Server_port' + }, + 'QQ官方/沙盒/V2': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token' + }, + 'QQ官方/沙盒/V2/指定intents': { + 'AppID': 'edit_root_Entry_ID', + 'AppSecret': 'edit_root_Entry_Server_access_token', + 'intents': 'edit_root_Entry_Server_port' + }, + 'Telegram': { + 'TOKEN': 'edit_root_Entry_Server_access_token' + }, + 'Discord': { + 'TOKEN': 'edit_root_Entry_Server_access_token' + }, + 'Discord/指定intents': { + 'TOKEN': 'edit_root_Entry_Server_access_token', + 'intents': 'edit_root_Entry_Server_port' + }, + '渡渡语音/Dodo/V2': { + 'BotID': 'edit_root_Entry_ID', + 'Bot私钥': 'edit_root_Entry_Server_access_token' + }, + '渡渡语音/Dodo/V1': { + 'BotID': 'edit_root_Entry_ID', + 'Bot私钥': 'edit_root_Entry_Server_access_token' + }, + 'Fanbook': { + 'Token': 'edit_root_Entry_Server_access_token' + }, + 'Hack.Chat': { + '房间名称': 'edit_root_Entry_Server_host', + 'Bot名称': 'edit_root_Entry_Server_access_token', + '密码': 'edit_root_Entry_Password' + }, + 'Hack.Chat/私有': { + '房间名称': 'edit_root_Entry_Server_host', + 'Bot名称': 'edit_root_Entry_Server_access_token', + '密码': 'edit_root_Entry_Password' + }, + '虚拟终端': { + '账号': 'edit_root_Entry_ID' + }, + '接口终端': { + '账号': 'edit_root_Entry_ID', + '端口': 'edit_root_Entry_Server_port' + }, + 'FF14终端': { + '账号': 'edit_root_Entry_ID', + '端口': 'edit_root_Entry_Server_port', + '回调端口': 'edit_root_Entry_Server_access_token' + }, + "钉钉": { + "Robot Code": 'edit_root_Entry_ID' + }, + '自定义': { + 'ID': 'edit_root_Entry_ID', + 'PASSWORD': 'edit_root_Entry_Password', + 'HOST': 'edit_root_Entry_Server_host', + 'PORT': 'edit_root_Entry_Server_port', + 'TOKEN': 'edit_root_Entry_Server_access_token' + }, + }, + 'platform_list': OlivOS.accountMetadataAPI.accountTypeDataList_platform, + 'platform_sdk_list': OlivOS.accountMetadataAPI.accountTypeDataList_platform_sdk, + 'platform_sdk_model_list': OlivOS.accountMetadataAPI.accountTypeDataList_platform_sdk_model, + }, + 'edit_root_Combobox_Server_auto_list': OlivOS.accountMetadataAPI.accountTypeDataList_server_auto, + 'edit_root_Combobox_Server_type_list': OlivOS.accountMetadataAPI.accountTypeDataList_server_type + } + # 此处进行type_mapping_list的拼合 + tmp_type_mapping_list = {} + for key_this in OlivOS.accountMetadataAPI.accountTypeMappingList: + tmp_mapping_slot = copy.deepcopy(OlivOS.accountMetadataAPI.accountTypeMappingList[key_this]) + tmp_Entry_slot = None + if key_this in self.UIData['edit_root_Combobox_dict']['type_mapping_list_Entry_slot']: + tmp_Entry_slot = self.UIData['edit_root_Combobox_dict']['type_mapping_list_Entry_slot'][key_this] + else: + tmp_Entry_slot = {} + tmp_mapping_slot.append(copy.deepcopy(tmp_Entry_slot)) + tmp_type_mapping_list[key_this] = tmp_mapping_slot + self.UIData['edit_root_Combobox_dict']['type_mapping_list'] = tmp_type_mapping_list def tree_edit_commit(self): miss_key_list = None @@ -902,6 +929,25 @@ def tree_edit_commit(self): tmp_port = '58001' if tmp_access_token == '': tmp_access_token = 'NONEED' + if tmp_platform_platform == 'qq' \ + and tmp_platform_sdk == 'onebot' \ + and tmp_platform_model in OlivOS.OPQBotLinkServerAPI.gCheckList \ + and tmp_server_auto == 'False': + if tmp_host == '': + tmp_host = '127.0.0.1' + if tmp_access_token == '': + tmp_access_token = 'NONEED' + if tmp_platform_platform == 'qq' \ + and tmp_platform_sdk == 'onebot' \ + and tmp_platform_model in OlivOS.OPQBotLinkServerAPI.gCheckList \ + and tmp_server_auto == 'True': + if tmp_host == '': + tmp_host = '127.0.0.1' + if tmp_platform_model in [ + 'opqbot_auto' + ]: + if tmp_port == '': + tmp_port = '8086' if tmp_platform_platform == 'qqGuild' \ and tmp_platform_sdk == 'qqGuild_link': if tmp_password == '': @@ -916,8 +962,13 @@ def tree_edit_commit(self): tmp_password = 'NONEED' if tmp_host == '': tmp_host = 'NONEED' - if tmp_port == '': - tmp_port = '0' + if tmp_platform_model not in [ + 'public_intents', + 'private_intents', + 'sandbox_intents' + ]: + if tmp_port == '': + tmp_port = '0' if tmp_platform_platform == 'mhyVila' \ and tmp_platform_sdk == 'mhyVila_link': tmp_id = tmp_id.strip('\n') @@ -955,8 +1006,11 @@ def tree_edit_commit(self): tmp_password = 'NONEED' if tmp_host == '': tmp_host = 'NONEED' - if tmp_port == '': - tmp_port = '0' + if tmp_platform_model not in [ + 'intents' + ]: + if tmp_port == '': + tmp_port = '0' if tmp_platform_platform == 'kaiheila' \ and tmp_platform_sdk == 'kaiheila_link': if tmp_id == '': @@ -1096,8 +1150,7 @@ def tree_edit_commit(self): tmp_offset += 1 if type_this is not None \ and type_this in self.UIData['edit_root_Combobox_dict']['type_qsign_array_note_list']: - tmp_res_bot_info.extends['qsign-server-protocal'] = self.UIData[ - 'edit_root_Combobox_qsign_protocal_StringVar'].get() + tmp_res_bot_info.extends['qsign-server-protocal'] = self.UIData['edit_root_Combobox_qsign_protocal_StringVar'].get() tmp_res_bot_info.extends['qsign-server'] = [] for tmp_i in range(self.UIData['edit_root_Entry_qsign_num']): key_pare = [ @@ -1105,6 +1158,8 @@ def tree_edit_commit(self): 'edit_root_Entry_qsign_key_%d' % tmp_i ] tmp_data = {} + tmp_data['addr'] = '' + tmp_data['key'] = '' if key_pare[0] + '_StringVar' in self.UIData: tmp_data['addr'] = self.UIData[key_pare[0] + '_StringVar'].get() if key_pare[1] + '_StringVar' in self.UIData: @@ -1299,10 +1354,12 @@ def tree_edit_UI_Combobox_init(self, obj_root, obj_name, str_name, x, y, width, height=height ) self.UIObject[obj_name].configure(state='readonly') - self.UIObject[obj_name].bind('<>', - lambda x: self.tree_edit_UI_Combobox_ComboboxSelected(x, action, obj_name)) + self.UIObject[obj_name].bind( + '<>', + lambda x: self.tree_edit_UI_Combobox_ComboboxSelected(x, action, obj_name) + ) - def tree_edit_UI_Combobox_ComboboxSelected(self, action, event, target): + def tree_edit_UI_Combobox_ComboboxSelected(self, event, action, target): if target == 'edit_root_Combobox_Account_type': self.tree_edit_UI_Combobox_update(action, 'type') elif target == 'edit_root_Combobox_platform': @@ -1311,6 +1368,8 @@ def tree_edit_UI_Combobox_ComboboxSelected(self, action, event, target): self.tree_edit_UI_Combobox_update(action, 'sdk') elif target == 'edit_root_Combobox_model': self.tree_edit_UI_Combobox_update(action, 'model') + elif target == 'edit_root_Combobox_qsign_protocal': + self.tree_edit_UI_Combobox_update(action, 'qsign_protocal') def tree_edit_UI_type_clear_note_GEN(self, tmp_type:str): def tree_edit_UI_type_clear_note(): @@ -1332,7 +1391,7 @@ def tree_edit_UI_qsign_list_set(): self.UIData['edit_root_Entry_qsign_num'] = 1 if self.UIData['edit_root_Entry_qsign_num'] > 10: self.UIData['edit_root_Entry_qsign_num'] = 10 - self.tree_edit_UI_Combobox_update(self.action, 'type') + self.tree_edit_UI_Combobox_update(self.action, 'qsign_protocal') return tree_edit_UI_qsign_list_set def tree_edit_UI_Combobox_update(self, action, con_action): @@ -1401,7 +1460,7 @@ def tree_edit_UI_Combobox_update(self, action, con_action): self.UIData['edit_root_Combobox_dict']['type_list'].index('自定义') ) tmp_type = self.UIObject['edit_root_Combobox_Account_type'].get() - if con_action in ['init', 'type', 'platform', 'sdk', 'model']: + if con_action in ['init', 'type', 'platform', 'sdk', 'model', 'qsign_protocal']: if tmp_type in self.UIData['edit_root_Combobox_dict']['type_mapping_list']: count = 1 if tmp_type != '自定义': @@ -1484,9 +1543,9 @@ def tree_edit_UI_Combobox_update(self, action, con_action): self.tree_edit_UI_Label_init( obj_root='edit_root', obj_name='edit_root_Label_type_note', - x=100, + x=15, y=40 + count * (24 + 6), - width=200, + width=400 - 15 * 2, height=24, title=self.UIData['edit_root_Combobox_dict']['type_note_list'][tmp_type] ) @@ -1514,7 +1573,7 @@ def tree_edit_UI_Combobox_update(self, action, con_action): y=40 + count * (24 + 6), width=200, height=24, - title='QSign服务器列表' + title='Qsign 服务器设置' ) count += 1 self.tree_edit_UI_Combobox_init( @@ -1531,6 +1590,7 @@ def tree_edit_UI_Combobox_update(self, action, con_action): self.UIObject['edit_root_Combobox_qsign_protocal']['value'] = tuple( self.UIData['edit_root_Combobox_qsign_protocal_list'] ) + tmp_qsign_protocal = self.UIData['edit_root_Combobox_qsign_protocal_StringVar'].get() self.UIObject['edit_root_Combobox_qsign_protocal'].current(0) if con_action == 'init': if action == 'update': @@ -1544,63 +1604,89 @@ def tree_edit_UI_Combobox_update(self, action, con_action): Account_data_this.extends['qsign-server-protocal'] ) ) + if con_action == 'qsign_protocal': + if action == 'update': + if tmp_qsign_protocal in self.UIData['edit_root_Combobox_qsign_protocal_list']: + self.UIObject['edit_root_Combobox_qsign_protocal'].current( + self.UIData['edit_root_Combobox_qsign_protocal_list'].index( + tmp_qsign_protocal + ) + ) + tmp_qsign_protocal = self.UIData['edit_root_Combobox_qsign_protocal_StringVar'].get() count += 1 - self.tree_UI_Button_init( - name='edit_root_Button_qsign_list_set_+', - text='增加一行', - command=self.tree_edit_UI_qsign_list_set_GEN('+'), - x=310, - y=40 + 5 * (24 + 6), - width=70, - height=24 - ) - self.tree_UI_Button_init( - name='edit_root_Button_qsign_list_set_-', - text='减少一行', - command=self.tree_edit_UI_qsign_list_set_GEN('-'), - x=310, - y=40 + 6 * (24 + 6), - width=70, - height=24 - ) - array_num = self.UIData['edit_root_Entry_qsign_num'] - for tmp_i in range(array_num): - key_pare = [ - 'edit_root_Entry_qsign_addr_%d' % tmp_i, - 'edit_root_Entry_qsign_key_%d' % tmp_i - ] - self.UIData['edit_root_Entry_qsign_list'].append(key_pare[0]) - self.UIData['edit_root_Entry_qsign_list'].append(key_pare[1]) - if key_pare[0] + '_StringVar' not in self.UIData: - self.UIData[key_pare[0] + '_StringVar'] = tkinter.StringVar() - self.tree_edit_UI_Entry_init( + if tmp_qsign_protocal in self.UIData['edit_root_Combobox_qsign_protocal_list_exemod']: + tmp_note_name = 'edit_root_Label_qsign_note_astalqsign_will_set' + if tmp_note_name not in self.UIData['edit_root_Entry_qsign_list']: + self.UIData['edit_root_Entry_qsign_list'].append(tmp_note_name) + self.tree_edit_UI_Label_init( obj_root='edit_root', - obj_name=key_pare[0], - str_name=key_pare[0] + '_StringVar', - x=100, + obj_name=tmp_note_name, + x=15, y=40 + count * (24 + 6), - width=200, + width=400 - 15 * 2, height=24, - action=self.action, - title='地址', - mode='NONE' + title='OlivOS 将会同步为你自动配置本地 AstralQsign 服务' ) count += 1 - if key_pare[1] + '_StringVar' not in self.UIData: - self.UIData[key_pare[1] + '_StringVar'] = tkinter.StringVar() - self.tree_edit_UI_Entry_init( - obj_root='edit_root', - obj_name=key_pare[1], - str_name=key_pare[1] + '_StringVar', - x=100, - y=40 + count * (24 + 6), - width=200, - height=24, - action=self.action, - title='KEY', - mode='NONE' + else: + self.tree_UI_Button_init( + name='edit_root_Button_qsign_list_set_+', + text='增加一行', + command=self.tree_edit_UI_qsign_list_set_GEN('+'), + x=310, + y=40 + 5 * (24 + 6), + width=70, + height=24 ) - count += 1 + self.tree_UI_Button_init( + name='edit_root_Button_qsign_list_set_-', + text='减少一行', + command=self.tree_edit_UI_qsign_list_set_GEN('-'), + x=310, + y=40 + 6 * (24 + 6), + width=70, + height=24 + ) + array_num = self.UIData['edit_root_Entry_qsign_num'] + for tmp_i in range(array_num): + key_pare = [ + 'edit_root_Entry_qsign_addr_%d' % tmp_i, + 'edit_root_Entry_qsign_key_%d' % tmp_i + ] + if key_pare[0] not in self.UIData['edit_root_Entry_qsign_list']: + self.UIData['edit_root_Entry_qsign_list'].append(key_pare[0]) + if key_pare[1] not in self.UIData['edit_root_Entry_qsign_list']: + self.UIData['edit_root_Entry_qsign_list'].append(key_pare[1]) + if key_pare[0] + '_StringVar' not in self.UIData: + self.UIData[key_pare[0] + '_StringVar'] = tkinter.StringVar() + self.tree_edit_UI_Entry_init( + obj_root='edit_root', + obj_name=key_pare[0], + str_name=key_pare[0] + '_StringVar', + x=100, + y=40 + count * (24 + 6), + width=200, + height=24, + action=self.action, + title='地址', + mode='NONE' + ) + count += 1 + if key_pare[1] + '_StringVar' not in self.UIData: + self.UIData[key_pare[1] + '_StringVar'] = tkinter.StringVar() + self.tree_edit_UI_Entry_init( + obj_root='edit_root', + obj_name=key_pare[1], + str_name=key_pare[1] + '_StringVar', + x=100, + y=40 + count * (24 + 6), + width=200, + height=24, + action=self.action, + title='KEY', + mode='NONE' + ) + count += 1 count -= 1 self.UIObject['edit_root'].geometry('400x%s' % (count * (24 + 6) + 100 + 10)) count += 1 diff --git a/OlivOS/nativeWinUIAPI.py b/OlivOS/nativeGUI/nativeWinUIAPI.py similarity index 69% rename from OlivOS/nativeWinUIAPI.py rename to OlivOS/nativeGUI/nativeWinUIAPI.py index 8b8fc527..449b2e6b 100644 --- a/OlivOS/nativeWinUIAPI.py +++ b/OlivOS/nativeGUI/nativeWinUIAPI.py @@ -85,6 +85,12 @@ def __init__( self.UIObject['root_cwcb_terminal'] = {} self.UIObject['root_cwcb_terminal_data'] = {} self.UIObject['root_cwcb_terminal_data_max'] = 500 + self.UIObject['root_opqbot_terminal'] = {} + self.UIObject['root_opqbot_terminal_data'] = {} + self.UIObject['root_opqbot_terminal_data_max'] = 500 + self.UIObject['root_napcat_terminal'] = {} + self.UIObject['root_napcat_terminal_data'] = {} + self.UIObject['root_napcat_terminal_data_max'] = 500 self.UIObject['root_virtual_terminal_terminal'] = {} self.UIObject['root_virtual_terminal_terminal_data'] = {} self.UIObject['root_virtual_terminal_terminal_data_max'] = 150 @@ -99,6 +105,8 @@ def __init__( self.UIData['shallow_gocqhttp_menu_list'] = None self.UIData['shallow_walleq_menu_list'] = None self.UIData['shallow_cwcb_menu_list'] = None + self.UIData['shallow_opqbot_menu_list'] = None + self.UIData['shallow_napcat_menu_list'] = None self.UIData['shallow_virtual_terminal_menu_list'] = None self.UIData['shallow_plugin_data_dict'] = None self.updateShallowMenuList() @@ -122,6 +130,8 @@ def on_control_rx(self, packet): if 'data' in packet.key['data'] \ and type(packet.key['data']['data']) is dict: self.bot_info = packet.key['data']['data'] + self.UIData['shallow_napcat_menu_list'] = None + self.UIData['shallow_opqbot_menu_list'] = None self.UIData['shallow_gocqhttp_menu_list'] = None self.UIData['shallow_walleq_menu_list'] = None self.UIData['shallow_cwcb_menu_list'] = None @@ -182,6 +192,64 @@ def mainrun(self): if self.UIObject['root_OlivOS_terminal'] is not None: self.UIObject['root_OlivOS_terminal'].tree_add_line( rx_packet_data.key['data']['data']) + elif 'napcat' == rx_packet_data.key['data']['action']: + if 'event' in rx_packet_data.key['data']: + if 'init' == rx_packet_data.key['data']['event']: + if self.UIData['shallow_napcat_menu_list'] is None: + self.UIData['shallow_napcat_menu_list'] = [] + if 'hash' in rx_packet_data.key['data']: + if rx_packet_data.key['data']['hash'] in self.bot_info: + tmp_title = '%s' % ( + str(self.bot_info[rx_packet_data.key['data']['hash']].id) + ) + self.UIData['shallow_napcat_menu_list'].append( + [ + tmp_title, + rx_packet_data.key['data']['hash'], + '', + 'napcat' + ] + ) + self.updateShallowMenuList() + if self.UIObject['root_shallow'] is not None: + self.updateShallow() + self.startNapCatTerminalUISend(rx_packet_data.key['data']['hash']) + elif 'log' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data'] and 'data' in \ + rx_packet_data.key['data']: + hash = rx_packet_data.key['data']['hash'] + if hash not in self.UIObject['root_napcat_terminal_data']: + self.UIObject['root_napcat_terminal_data'][hash] = [] + self.UIObject['root_napcat_terminal_data'][hash].append( + rx_packet_data.key['data']['data']) + if len(self.UIObject['root_napcat_terminal_data'][hash]) > \ + self.UIObject['root_napcat_terminal_data_max']: + self.UIObject['root_napcat_terminal_data'][hash].pop(0) + if hash in self.UIObject['root_napcat_terminal']: + self.UIObject['root_napcat_terminal'][hash].tree_add_line( + rx_packet_data.key['data']['data']) + elif 'qrcode' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data'] and 'path' in \ + rx_packet_data.key['data']: + hash = rx_packet_data.key['data']['hash'] + if hash in self.bot_info: + if hash in self.UIObject['root_qrcode_window']: + try: + self.UIObject['root_qrcode_window'][hash].stop() + except: + pass + self.UIObject['root_qrcode_window'][hash] = QRcodeUI( + Model_name='qrcode_window', + logger_proc=self.Proc_info.logger_proc.log, + root=self, + root_tk=None, + bot=self.bot_info[hash], + path=rx_packet_data.key['data']['path'] + ) + self.UIObject['root_qrcode_window'][hash].start() + elif 'napcat_terminal_on' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data']: + self.startNapCatTerminalUI(rx_packet_data.key['data']['hash']) elif 'gocqhttp' == rx_packet_data.key['data']['action']: if 'event' in rx_packet_data.key['data']: if 'init' == rx_packet_data.key['data']['event']: @@ -190,8 +258,7 @@ def mainrun(self): if 'hash' in rx_packet_data.key['data']: if rx_packet_data.key['data']['hash'] in self.bot_info: tmp_title = '%s' % ( - str(self.bot_info[ - rx_packet_data.key['data']['hash']].id) + str(self.bot_info[rx_packet_data.key['data']['hash']].id) ) self.UIData['shallow_gocqhttp_menu_list'].append( [ @@ -238,6 +305,14 @@ def mainrun(self): path=rx_packet_data.key['data']['path'] ) self.UIObject['root_qrcode_window'][hash].start() + elif 'token_get' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data'] \ + and 'token' in rx_packet_data.key['data']: + hash = rx_packet_data.key['data']['hash'] + self.setGoCqhttpModelSend( + hash=rx_packet_data.key['data']['hash'], + data=rx_packet_data.key['data']['token'] + ) elif 'gocqhttp_terminal_on' == rx_packet_data.key['data']['event']: if 'hash' in rx_packet_data.key['data']: self.startGoCqhttpTerminalUI(rx_packet_data.key['data']['hash']) @@ -249,8 +324,7 @@ def mainrun(self): if 'hash' in rx_packet_data.key['data']: if rx_packet_data.key['data']['hash'] in self.bot_info: tmp_title = '%s' % ( - str(self.bot_info[ - rx_packet_data.key['data']['hash']].id) + str(self.bot_info[rx_packet_data.key['data']['hash']].id) ) self.UIData['shallow_walleq_menu_list'].append( [ @@ -308,8 +382,7 @@ def mainrun(self): if 'hash' in rx_packet_data.key['data']: if rx_packet_data.key['data']['hash'] in self.bot_info: tmp_title = '%s' % ( - str(self.bot_info[ - rx_packet_data.key['data']['hash']].id) + str(self.bot_info[rx_packet_data.key['data']['hash']].id) ) self.UIData['shallow_cwcb_menu_list'].append( [ @@ -340,6 +413,52 @@ def mainrun(self): elif 'cwcb_terminal_on' == rx_packet_data.key['data']['event']: if 'hash' in rx_packet_data.key['data']: self.startCWCBTerminalUI(rx_packet_data.key['data']['hash']) + elif 'opqbot' == rx_packet_data.key['data']['action']: + if 'event' in rx_packet_data.key['data']: + if 'init' == rx_packet_data.key['data']['event']: + if self.UIData['shallow_opqbot_menu_list'] is None: + self.UIData['shallow_opqbot_menu_list'] = [] + if 'hash' in rx_packet_data.key['data']: + if rx_packet_data.key['data']['hash'] in self.bot_info: + tmp_title = '%s' % ( + str(self.bot_info[rx_packet_data.key['data']['hash']].id) + ) + self.UIData['shallow_opqbot_menu_list'].append( + [ + tmp_title, + rx_packet_data.key['data']['hash'], + '', + 'opqbot' + ] + ) + self.updateShallowMenuList() + if self.UIObject['root_shallow'] is not None: + self.updateShallow() + self.startOPQBotTerminalUISend(rx_packet_data.key['data']['hash']) + elif 'log' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data'] and 'data' in \ + rx_packet_data.key['data']: + hash = rx_packet_data.key['data']['hash'] + if hash not in self.UIObject['root_opqbot_terminal_data']: + self.UIObject['root_opqbot_terminal_data'][hash] = [] + self.UIObject['root_opqbot_terminal_data'][hash].append( + rx_packet_data.key['data']['data']) + if len(self.UIObject['root_opqbot_terminal_data'][hash]) > \ + self.UIObject['root_opqbot_terminal_data_max']: + self.UIObject['root_opqbot_terminal_data'][hash].pop(0) + if hash in self.UIObject['root_opqbot_terminal']: + self.UIObject['root_opqbot_terminal'][hash].tree_add_line( + rx_packet_data.key['data']['data']) + elif 'qrcode' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data'] \ + and 'path' in rx_packet_data.key['data']: + hash = rx_packet_data.key['data']['hash'] + path = rx_packet_data.key['data']['path'] + #print(rx_packet_data.key['data']) + self.sendOpenQRcodeUrl(hash, path) + elif 'opqbot_terminal_on' == rx_packet_data.key['data']['event']: + if 'hash' in rx_packet_data.key['data']: + self.startOPQBotTerminalUI(rx_packet_data.key['data']['hash']) elif 'virtual_terminal' == rx_packet_data.key['data']['action']: if 'event' in rx_packet_data.key['data']: if 'init' == rx_packet_data.key['data']['event']: @@ -348,8 +467,7 @@ def mainrun(self): if 'hash' in rx_packet_data.key['data']: if rx_packet_data.key['data']['hash'] in self.bot_info: tmp_title = '%s' % ( - str(self.bot_info[ - rx_packet_data.key['data']['hash']].id) + str(self.bot_info[rx_packet_data.key['data']['hash']].id) ) self.UIData['shallow_virtual_terminal_menu_list'].append( [ @@ -402,7 +520,9 @@ def updateShallowMenuList(self): self.UIData['shallow_menu_list'] = [ ['打开终端', self.startOlivOSTerminalUISend], #['账号管理', self.startAccountEditSendFunc()], - ['账号管理', None], + #['账号管理', None], + ['NapCat管理', self.UIData['shallow_napcat_menu_list']], + ['OPQBot管理', self.UIData['shallow_opqbot_menu_list']], ['gocqhttp管理', self.UIData['shallow_gocqhttp_menu_list']], ['walleq管理', self.UIData['shallow_walleq_menu_list']], ['ComWeChat管理', self.UIData['shallow_cwcb_menu_list']], @@ -415,12 +535,12 @@ def updateShallowMenuList(self): ['退出OlivOS', self.setOlivOSExit] ] for data_this in self.UIData['shallow_menu_list']: - if data_this[0] in ['gocqhttp管理', 'walleq管理', 'ComWeChat管理', '虚拟终端']: + if data_this[0] in ['NapCat管理', 'OPQBot管理', 'gocqhttp管理', 'walleq管理', 'ComWeChat管理', '虚拟终端']: if data_this[1] is not None: tmp_new.append(data_this) elif data_this[0] in ['更新OlivOS']: if self.UIObject['flag_have_update']: - data_this[0] = data_this[0] + '[有更新!]' + data_this[0] += '[有更新!]' tmp_new.append(data_this) else: tmp_new.append(data_this) @@ -456,6 +576,16 @@ def resFunc(): self.startCWCBTerminalUISend(hash) return resFunc + def startOPQBotTerminalUISendFunc(self, hash): + def resFunc(): + self.startOPQBotTerminalUISend(hash) + return resFunc + + def startNapCatTerminalUISendFunc(self, hash): + def resFunc(): + self.startNapCatTerminalUISend(hash) + return resFunc + def startGoCqhttpTerminalUISend(self, hash): self.sendRxEvent( 'send', { @@ -498,6 +628,34 @@ def startCWCBTerminalUISend(self, hash): } ) + def startOPQBotTerminalUISend(self, hash): + self.sendRxEvent( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'opqbot', + 'event': 'opqbot_terminal_on', + 'hash': hash, + } + } + ) + + def startNapCatTerminalUISend(self, hash): + self.sendRxEvent( + 'send', { + 'target': { + 'type': 'nativeWinUI' + }, + 'data': { + 'action': 'napcat', + 'event': 'napcat_terminal_on', + 'hash': hash, + } + } + ) + def startGoCqhttpTerminalUI(self, hash): if hash in self.bot_info: if hash in self.UIObject['root_gocqhttp_terminal']: @@ -546,6 +704,38 @@ def startCWCBTerminalUI(self, hash): ) self.UIObject['root_cwcb_terminal'][hash].start() + def startOPQBotTerminalUI(self, hash): + if hash in self.bot_info: + if hash in self.UIObject['root_opqbot_terminal']: + try: + self.UIObject['root_opqbot_terminal'][hash].stop() + except: + pass + self.UIObject['root_opqbot_terminal'][hash] = opqbotTerminalUI( + Model_name='opqbot_terminal', + logger_proc=self.Proc_info.logger_proc.log, + root=self, + root_tk=None, + bot=self.bot_info[hash] + ) + self.UIObject['root_opqbot_terminal'][hash].start() + + def startNapCatTerminalUI(self, hash): + if hash in self.bot_info: + if hash in self.UIObject['root_napcat_terminal']: + try: + self.UIObject['root_napcat_terminal'][hash].stop() + except: + pass + self.UIObject['root_napcat_terminal'][hash] = napcatTerminalUI( + Model_name='napcat_terminal', + logger_proc=self.Proc_info.logger_proc.log, + root=self, + root_tk=None, + bot=self.bot_info[hash] + ) + self.UIObject['root_napcat_terminal'][hash].start() + def startVirtualTerminalUISendFunc(self, hash): def resFunc(): self.startVirtualTerminalUISend(hash) @@ -635,7 +825,33 @@ def setWalleQModelSend(self, hash, data): def setCWCBModelSend(self, hash, data): self.sendControlEventSend('send', { 'target': { - 'type': 'walleq_lib_exe_model', + 'type': 'cwcb_lib_exe_model', + 'hash': hash + }, + 'data': { + 'action': 'input', + 'data': data + } + } + ) + + def setOPQBotModelSend(self, hash, data): + self.sendControlEventSend('send', { + 'target': { + 'type': 'opqbot_lib_exe_model', + 'hash': hash + }, + 'data': { + 'action': 'input', + 'data': data + } + } + ) + + def setNapCatModelSend(self, hash, data): + self.sendControlEventSend('send', { + 'target': { + 'type': 'napcat_lib_exe_model', 'hash': hash }, 'data': { @@ -775,6 +991,18 @@ def sendOpenForum(self): ) self.sendOpenWebviewEvent('forum_page', 'OlivOS论坛', 'https://forum.olivos.run/') + def sendOpenQRcodeUrl(self, hash, url): + if type(self.bot_info) is dict \ + and hash in self.bot_info: + try: + res = tkinter.messagebox.askquestion(f'请使用账号 {self.bot_info[hash].id} 扫码', "是否使用内置浏览器?") + if res == 'yes': + self.sendOpenWebviewEvent(f'qrcode_page={hash}', f'请使用账号 {self.bot_info[hash].id} 扫码', url) + else: + webbrowser.open(url) + except webbrowser.Error as error_info: + tkinter.messagebox.showerror("webbrowser.Error", error_info) + def sendOpenWebviewEvent( self, name:str, @@ -884,7 +1112,8 @@ def start(self): self.UIObject['root'].grid_rowconfigure(1, weight=0) self.UIObject['root'].grid_columnconfigure(0, weight=0) self.UIObject['root'].grid_columnconfigure(1, weight=2) - self.UIObject['root'].grid_columnconfigure(2, weight=0) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) self.UIObject['root'].resizable( width=True, height=True @@ -909,7 +1138,7 @@ def start(self): column=0, sticky="nsew", rowspan=1, - columnspan=2, + columnspan=3, padx=(15, 0), pady=(15, 0), ipadx=0, @@ -928,7 +1157,7 @@ def start(self): # ) self.UIObject['tree_yscroll'].grid( row=0, - column=2, + column=3, sticky="nsw", rowspan=1, columnspan=1, @@ -937,8 +1166,9 @@ def start(self): ipadx=0, ipady=0 ) + self.UIData['flag_tree_is_bottom'] = True self.UIObject['tree'].configure( - yscrollcommand=self.UIObject['tree_yscroll'].set + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) ) self.root_Entry_init( @@ -956,15 +1186,37 @@ def start(self): self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) self.UIObject['root_input'].grid( row=1, - column=1, + column=0, sticky="s", rowspan=1, - columnspan=3, - padx=(15, 15), + columnspan=2, + padx=(15, 0), pady=(8, 15), ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, ipady=0 ) + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) @@ -974,6 +1226,15 @@ def start(self): self.exit() + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + def tree_rightKey(self, event): # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 # iid = self.UIObject['tree'].identify_row(event.y) @@ -997,18 +1258,21 @@ def rightKey_action(self, action: str): self.UIObject['root'].update() def root_Entry_enter_Func(self, name): - def resFunc(event): - self.root_Entry_enter(name, event) + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) return resFunc def root_Entry_enter(self, name, event): if name == 'root_input': - input = self.UIData['root_input_StringVar'].get() - if len(input) >= 0 and len(input) < 1000: - self.root.setGoCqhttpModelSend(self.bot.hash, input) + input_data = self.UIData['root_input_StringVar'].get() + if len(input_data) >= 0 and len(input_data) < 1000: + self.root_setGoCqhttpModelSend(input_data) self.UIData['root_input_StringVar'].set('') + def root_setGoCqhttpModelSend(self, input_data): + self.root.setGoCqhttpModelSend(self.bot.hash, input_data) + def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', mode='NONE'): self.UIObject[obj_name + '=Label'] = tkinter.Label( @@ -1028,7 +1292,8 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he self.UIData[str_name] = tkinter.StringVar() self.UIObject[obj_name] = tkinter.Entry( self.UIObject[obj_root], - textvariable=self.UIData[str_name] + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') ) self.UIObject[obj_name].configure( bg=self.UIConfig['color_004'], @@ -1049,6 +1314,21 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he # height = height # ) + def show_tx_url_webbrowser(self, url): + res = tkinter.messagebox.askquestion("请完成验证", "是否使用内置人机验证助手访问 \"" + url + "\" ?") + try: + if res == 'yes': + OlivOS.libEXEModelAPI.sendOpentxTuringTestPage( + 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) + def show_url_webbrowser(self, url): res = tkinter.messagebox.askquestion("请完成验证", "是否通过浏览器访问 \"" + url + "\" ?") try: @@ -1087,34 +1367,658 @@ def tree_add_line(self, data, flagInit = False): res_data ) ) - self.UIObject['tree'].see(iid) - self.UIObject['tree'].update() + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass if not flagInit and platform.system() == 'Windows': try: matchRes = re.match( - r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请前往该地址验证\s+->\s+(http[s]{0,1}://captcha\.go-cqhttp\.org/captcha\?[^\s]+).*$', + r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请选择提交滑块ticket方式:.*$', + res_data_raw + ) + if matchRes != None: + self.tree_add_line('=================================================================') + self.tree_add_line(' 【推荐】 选择手动抓取ticket方式将会允许OlivOS接管验证流程 【推荐】') + self.tree_add_line('=================================================================') + + matchRes = re.match( + r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请前往该地址验证\s+->\s+(http[s]{0,1}://ti\.qq\.com/safe/tools/captcha/sms-verify-login\?[^\s]+).*$', + res_data_raw + ) + if matchRes != None: + matchResList = list(matchRes.groups()) + if len(matchResList) == 1: + matchResUrl = matchResList[0] + self.show_tx_url_webbrowser(matchResUrl) + + matchRes = re.match( + r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s账号已开启设备锁,请前往\s+->\s+(http[s]{0,1}://accounts\.qq\.com/safe/verify[^\s]+).*$', res_data_raw ) - if matchRes != None: - matchResList = list(matchRes.groups()) - if len(matchResList) == 1: - matchResUrl = matchResList[0] - self.show_url_webbrowser(matchResUrl) + if matchRes != None: + matchResList = list(matchRes.groups()) + if len(matchResList) == 1: + matchResUrl = matchResList[0] + matchResUrl = matchResUrl.replace('accounts.qq.com/safe/verify', 'accounts.qq.com/safe/qrcode') + self.show_url_webbrowser(matchResUrl) + except: + pass + + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + + def stop(self): + self.exit() + self.UIObject['root'].destroy() + + def exit(self): + self.root.UIObject['root_gocqhttp_terminal'].pop(self.bot.hash) + + +class walleqTerminalUI(object): + def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=None): + self.Model_name = Model_name + self.root = root + self.root_tk = root_tk + self.bot = bot + self.UIObject = {} + self.UIData = {} + self.UIConfig = {} + self.logger_proc = logger_proc + self.UIConfig.update(dictColorContext) + + def start(self): + self.UIObject['root'] = tkinter.Toplevel() + self.UIObject['root'].title('WalleQ 终端 - %s' % str(self.bot.id)) + self.UIObject['root'].geometry('800x600') + self.UIObject['root'].minsize(800, 600) + self.UIObject['root'].grid_rowconfigure(0, weight=15) + self.UIObject['root'].grid_rowconfigure(1, weight=0) + self.UIObject['root'].grid_columnconfigure(0, weight=0) + self.UIObject['root'].grid_columnconfigure(1, weight=2) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) + self.UIObject['root'].resizable( + width=True, + height=True + ) + self.UIObject['root'].configure(bg=self.UIConfig['color_001']) + + self.UIObject['style'] = ttk.Style() + fix_Treeview_color(self.UIObject['style']) + + self.UIObject['tree'] = ttk.Treeview(self.UIObject['root']) + self.UIObject['tree']['show'] = 'headings' + self.UIObject['tree']['columns'] = ('DATA') + self.UIObject['tree'].column('DATA', width=800 - 15 * 2 - 18 - 5) + self.UIObject['tree'].heading('DATA', text='日志') + self.UIObject['tree']['selectmode'] = 'browse' + self.UIObject['tree_rightkey_menu'] = tkinter.Menu(self.UIObject['root'], tearoff=False) + self.UIObject['tree'].bind('', lambda x: self.tree_rightKey(x)) + # self.tree_load() + # self.UIObject['tree'].place(x = 15, y = 15, width = 800 - 15 * 2 - 18 , height = 600 - 15 * 2 - 24 - 8) + self.UIObject['tree'].grid( + row=0, + column=0, + sticky="nsew", + rowspan=1, + columnspan=3, + padx=(15, 0), + pady=(15, 0), + ipadx=0, + ipady=0 + ) + self.UIObject['tree_yscroll'] = ttk.Scrollbar( + self.UIObject['root'], + orient="vertical", + command=self.UIObject['tree'].yview + ) + # self.UIObject['tree_yscroll'].place( + # x = 800 - 15 - 18, + # y = 15, + # width = 18, + # height = 600 - 15 * 2 - 24 - 8 + # ) + self.UIObject['tree_yscroll'].grid( + row=0, + column=3, + sticky="nsw", + rowspan=1, + columnspan=1, + padx=(0, 15), + pady=(15, 0), + ipadx=0, + ipady=0 + ) + self.UIData['flag_tree_is_bottom'] = True + self.UIObject['tree'].configure( + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) + ) + + self.root_Entry_init( + obj_root='root', + obj_name='root_input', + str_name='root_input_StringVar', + x=15, + y=600 - 15 * 1 - 24, + width_t=0, + width=800 - 15 * 2, + height=24, + action=None, + title='输入' + ) + self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) + self.UIObject['root_input'].grid( + row=1, + column=0, + sticky="s", + rowspan=1, + columnspan=2, + padx=(15, 0), + pady=(8, 15), + ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, + ipady=0 + ) + + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') + self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) + + self.tree_init_line() + + self.UIObject['root'].mainloop() + + self.exit() + + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + + def tree_rightKey(self, event): + # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 + # iid = self.UIObject['tree'].identify_row(event.y) + # self.UIObject['tree'].selection_set(iid) + # self.UIObject['tree'].update() + self.UIObject['tree_rightkey_menu'].delete(0, tkinter.END) + self.UIObject['tree_rightkey_menu'].add_command(label='查看', command=lambda: self.rightKey_action('show')) + self.UIObject['tree_rightkey_menu'].add_command(label='复制', command=lambda: self.rightKey_action('copy')) + self.UIObject['tree_rightkey_menu'].post(event.x_root, event.y_root) + + def rightKey_action(self, action: str): + if action == 'show': + msg = get_tree_force(self.UIObject['tree'])['text'] + if len(msg) > 0: + tkinter.messagebox.showinfo('日志内容', msg) + elif action == 'copy': + msg = get_tree_force(self.UIObject['tree'])['text'] + if len(msg) > 0: + self.UIObject['root'].clipboard_clear() + self.UIObject['root'].clipboard_append(msg) + self.UIObject['root'].update() + + def root_Entry_enter_Func(self, name): + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) + + return resFunc + + def root_Entry_enter(self, name, event): + if name == 'root_input': + input = self.UIData['root_input_StringVar'].get() + if len(input) >= 0 and len(input) < 1000: + self.root.setWalleQModelSend(self.bot.hash, input) + self.UIData['root_input_StringVar'].set('') + + def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', + mode='NONE'): + self.UIObject[obj_name + '=Label'] = tkinter.Label( + self.UIObject[obj_root], + text=title + ) + self.UIObject[obj_name + '=Label'].configure( + bg=self.UIConfig['color_001'], + fg=self.UIConfig['color_004'] + ) + # self.UIObject[obj_name + '=Label'].place( + # x = x - width_t, + # y = y, + # width = width_t, + # height = height + # ) + self.UIData[str_name] = tkinter.StringVar() + self.UIObject[obj_name] = tkinter.Entry( + self.UIObject[obj_root], + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') + ) + self.UIObject[obj_name].configure( + bg=self.UIConfig['color_004'], + fg=self.UIConfig['color_005'], + bd=0 + ) + if mode == 'SAFE': + self.UIObject[obj_name].configure( + show='●' + ) + self.UIObject[obj_name].configure( + width=width + ) + # self.UIObject[obj_name].place( + # x = x, + # y = y, + # width = width, + # height = height + # ) + + def show_url_webbrowser(self, url): + res = tkinter.messagebox.askquestion("请完成验证", "是否通过浏览器访问 \"" + url + "\" ?") + try: + if res == 'yes': + webbrowser.open(url) + except webbrowser.Error as error_info: + tkinter.messagebox.showerror("webbrowser.Error", error_info) + + def tree_init_line(self): + if self.bot.hash in self.root.UIObject['root_walleq_terminal_data']: + for line in self.root.UIObject['root_walleq_terminal_data'][self.bot.hash]: + self.tree_add_line(line, flagInit = True) + + def tree_add_line(self, data, flagInit = False): + res_data = re.sub(r'\033\[[\d;]*m?', '', data) + res_data_raw = res_data + res_data = res_data.encode(encoding='gb2312', errors='replace').decode(encoding='gb2312', errors='replace') + res_data_1 = res_data + res_data = res_data.replace(' ', '\ ') + if len(res_data.replace('\ ', '')) > 0: + try: + iid = self.UIObject['tree'].insert( + '', + tkinter.END, + text=res_data_1, + values=( + res_data + ) + ) + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() + except: + pass + + if not flagInit and platform.system() == 'Windows': + try: + matchRes = re.match( + r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请前往该地址验证\s+->\s+(http[s]{0,1}://captcha\.go-cqhttp\.org/captcha\?[^\s]+).*$', + res_data_raw + ) + if matchRes != None: + matchResList = list(matchRes.groups()) + if len(matchResList) == 1: + matchResUrl = matchResList[0] + self.show_url_webbrowser(matchResUrl) + except: + pass + + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + + def stop(self): + self.exit() + self.UIObject['root'].destroy() + + def exit(self): + self.root.UIObject['root_walleq_terminal'].pop(self.bot.hash) + + + +class CWCBTerminalUI(object): + def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=None): + self.Model_name = Model_name + self.root = root + self.root_tk = root_tk + self.bot = bot + self.UIObject = {} + self.UIData = {} + self.UIConfig = {} + self.logger_proc = logger_proc + self.UIConfig.update(dictColorContext) + + def start(self): + self.UIObject['root'] = tkinter.Toplevel() + self.UIObject['root'].title('ComWeChatBotClient 终端 - %s' % str(self.bot.id)) + self.UIObject['root'].geometry('800x600') + self.UIObject['root'].minsize(800, 600) + self.UIObject['root'].grid_rowconfigure(0, weight=15) + self.UIObject['root'].grid_rowconfigure(1, weight=0) + self.UIObject['root'].grid_columnconfigure(0, weight=0) + self.UIObject['root'].grid_columnconfigure(1, weight=2) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) + self.UIObject['root'].resizable( + width=True, + height=True + ) + self.UIObject['root'].configure(bg=self.UIConfig['color_001']) + + self.UIObject['style'] = ttk.Style() + fix_Treeview_color(self.UIObject['style']) + + self.UIObject['tree'] = ttk.Treeview(self.UIObject['root']) + self.UIObject['tree']['show'] = 'headings' + self.UIObject['tree']['columns'] = ('DATA') + self.UIObject['tree'].column('DATA', width=800 - 15 * 2 - 18 - 5) + self.UIObject['tree'].heading('DATA', text='日志') + self.UIObject['tree']['selectmode'] = 'browse' + self.UIObject['tree_rightkey_menu'] = tkinter.Menu(self.UIObject['root'], tearoff=False) + self.UIObject['tree'].bind('', lambda x: self.tree_rightKey(x)) + # self.tree_load() + # self.UIObject['tree'].place(x = 15, y = 15, width = 800 - 15 * 2 - 18 , height = 600 - 15 * 2 - 24 - 8) + self.UIObject['tree'].grid( + row=0, + column=0, + sticky="nsew", + rowspan=1, + columnspan=3, + padx=(15, 0), + pady=(15, 0), + ipadx=0, + ipady=0 + ) + self.UIObject['tree_yscroll'] = ttk.Scrollbar( + self.UIObject['root'], + orient="vertical", + command=self.UIObject['tree'].yview + ) + # self.UIObject['tree_yscroll'].place( + # x = 800 - 15 - 18, + # y = 15, + # width = 18, + # height = 600 - 15 * 2 - 24 - 8 + # ) + self.UIObject['tree_yscroll'].grid( + row=0, + column=3, + sticky="nsw", + rowspan=1, + columnspan=1, + padx=(0, 15), + pady=(15, 0), + ipadx=0, + ipady=0 + ) + self.UIData['flag_tree_is_bottom'] = True + self.UIObject['tree'].configure( + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) + ) + + self.root_Entry_init( + obj_root='root', + obj_name='root_input', + str_name='root_input_StringVar', + x=15, + y=600 - 15 * 1 - 24, + width_t=0, + width=800 - 15 * 2, + height=24, + action=None, + title='输入' + ) + self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) + self.UIObject['root_input'].grid( + row=1, + column=0, + sticky="s", + rowspan=1, + columnspan=2, + padx=(15, 0), + pady=(8, 15), + ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, + ipady=0 + ) + + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') + self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) + + self.tree_init_line() + + self.UIObject['root'].mainloop() + + self.exit() + + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + + def tree_rightKey(self, event): + # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 + # iid = self.UIObject['tree'].identify_row(event.y) + # self.UIObject['tree'].selection_set(iid) + # self.UIObject['tree'].update() + self.UIObject['tree_rightkey_menu'].delete(0, tkinter.END) + self.UIObject['tree_rightkey_menu'].add_command(label='查看', command=lambda: self.rightKey_action('show')) + self.UIObject['tree_rightkey_menu'].add_command(label='复制', command=lambda: self.rightKey_action('copy')) + self.UIObject['tree_rightkey_menu'].post(event.x_root, event.y_root) + + def rightKey_action(self, action: str): + if action == 'show': + msg = get_tree_force(self.UIObject['tree'])['text'] + if len(msg) > 0: + tkinter.messagebox.showinfo('日志内容', msg) + elif action == 'copy': + msg = get_tree_force(self.UIObject['tree'])['text'] + if len(msg) > 0: + self.UIObject['root'].clipboard_clear() + self.UIObject['root'].clipboard_append(msg) + self.UIObject['root'].update() + + def root_Entry_enter_Func(self, name): + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) + + return resFunc + + def root_Entry_enter(self, name, event): + if name == 'root_input': + input = self.UIData['root_input_StringVar'].get() + if len(input) >= 0 and len(input) < 1000: + self.root.setCWCBModelSend(self.bot.hash, input) + self.UIData['root_input_StringVar'].set('') + + def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', + mode='NONE'): + self.UIObject[obj_name + '=Label'] = tkinter.Label( + self.UIObject[obj_root], + text=title + ) + self.UIObject[obj_name + '=Label'].configure( + bg=self.UIConfig['color_001'], + fg=self.UIConfig['color_004'] + ) + # self.UIObject[obj_name + '=Label'].place( + # x = x - width_t, + # y = y, + # width = width_t, + # height = height + # ) + self.UIData[str_name] = tkinter.StringVar() + self.UIObject[obj_name] = tkinter.Entry( + self.UIObject[obj_root], + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') + ) + self.UIObject[obj_name].configure( + bg=self.UIConfig['color_004'], + fg=self.UIConfig['color_005'], + bd=0 + ) + if mode == 'SAFE': + self.UIObject[obj_name].configure( + show='●' + ) + self.UIObject[obj_name].configure( + width=width + ) + # self.UIObject[obj_name].place( + # x = x, + # y = y, + # width = width, + # height = height + # ) + + def tree_init_line(self): + if self.bot.hash in self.root.UIObject['root_cwcb_terminal_data']: + for line in self.root.UIObject['root_cwcb_terminal_data'][self.bot.hash]: + self.tree_add_line(line, flagInit = True) + + def tree_add_line(self, data, flagInit = False): + res_data = re.sub(r'\033\[[\d;]*m?', '', data) + res_data_raw = res_data + res_data = res_data.encode(encoding='gb2312', errors='replace').decode(encoding='gb2312', errors='replace') + res_data_1 = res_data + res_data = res_data.replace(' ', '\ ') + if len(res_data.replace('\ ', '')) > 0: + try: + iid = self.UIObject['tree'].insert( + '', + tkinter.END, + text=res_data_1, + values=( + res_data + ) + ) + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + def stop(self): self.exit() self.UIObject['root'].destroy() def exit(self): - self.root.UIObject['root_gocqhttp_terminal'].pop(self.bot.hash) + self.root.UIObject['root_cwcb_terminal'].pop(self.bot.hash) -class walleqTerminalUI(object): + +class opqbotTerminalUI(object): def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=None): self.Model_name = Model_name self.root = root @@ -1128,14 +2032,15 @@ def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=No def start(self): self.UIObject['root'] = tkinter.Toplevel() - self.UIObject['root'].title('WalleQ 终端 - %s' % str(self.bot.id)) + self.UIObject['root'].title('OPQBot 终端 - %s' % str(self.bot.id)) self.UIObject['root'].geometry('800x600') self.UIObject['root'].minsize(800, 600) self.UIObject['root'].grid_rowconfigure(0, weight=15) self.UIObject['root'].grid_rowconfigure(1, weight=0) self.UIObject['root'].grid_columnconfigure(0, weight=0) self.UIObject['root'].grid_columnconfigure(1, weight=2) - self.UIObject['root'].grid_columnconfigure(2, weight=0) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) self.UIObject['root'].resizable( width=True, height=True @@ -1160,7 +2065,7 @@ def start(self): column=0, sticky="nsew", rowspan=1, - columnspan=2, + columnspan=3, padx=(15, 0), pady=(15, 0), ipadx=0, @@ -1179,7 +2084,7 @@ def start(self): # ) self.UIObject['tree_yscroll'].grid( row=0, - column=2, + column=3, sticky="nsw", rowspan=1, columnspan=1, @@ -1188,8 +2093,9 @@ def start(self): ipadx=0, ipady=0 ) + self.UIData['flag_tree_is_bottom'] = True self.UIObject['tree'].configure( - yscrollcommand=self.UIObject['tree_yscroll'].set + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) ) self.root_Entry_init( @@ -1207,15 +2113,37 @@ def start(self): self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) self.UIObject['root_input'].grid( row=1, - column=1, + column=0, sticky="s", rowspan=1, - columnspan=3, - padx=(15, 15), + columnspan=2, + padx=(15, 0), pady=(8, 15), ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, ipady=0 ) + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) @@ -1225,6 +2153,15 @@ def start(self): self.exit() + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + def tree_rightKey(self, event): # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 # iid = self.UIObject['tree'].identify_row(event.y) @@ -1248,8 +2185,8 @@ def rightKey_action(self, action: str): self.UIObject['root'].update() def root_Entry_enter_Func(self, name): - def resFunc(event): - self.root_Entry_enter(name, event) + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) return resFunc @@ -1257,7 +2194,7 @@ def root_Entry_enter(self, name, event): if name == 'root_input': input = self.UIData['root_input_StringVar'].get() if len(input) >= 0 and len(input) < 1000: - self.root.setWalleQModelSend(self.bot.hash, input) + self.root.setOPQBotModelSend(self.bot.hash, input) self.UIData['root_input_StringVar'].set('') def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', @@ -1279,7 +2216,8 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he self.UIData[str_name] = tkinter.StringVar() self.UIObject[obj_name] = tkinter.Entry( self.UIObject[obj_root], - textvariable=self.UIData[str_name] + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') ) self.UIObject[obj_name].configure( bg=self.UIConfig['color_004'], @@ -1300,17 +2238,9 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he # height = height # ) - def show_url_webbrowser(self, url): - res = tkinter.messagebox.askquestion("请完成验证", "是否通过浏览器访问 \"" + url + "\" ?") - try: - if res == 'yes': - webbrowser.open(url) - except webbrowser.Error as error_info: - tkinter.messagebox.showerror("webbrowser.Error", error_info) - def tree_init_line(self): - if self.bot.hash in self.root.UIObject['root_walleq_terminal_data']: - for line in self.root.UIObject['root_walleq_terminal_data'][self.bot.hash]: + if self.bot.hash in self.root.UIObject['root_opqbot_terminal_data']: + for line in self.root.UIObject['root_opqbot_terminal_data'][self.bot.hash]: self.tree_add_line(line, flagInit = True) def tree_add_line(self, data, flagInit = False): @@ -1329,35 +2259,44 @@ def tree_add_line(self, data, flagInit = False): res_data ) ) - self.UIObject['tree'].see(iid) - self.UIObject['tree'].update() + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass - if not flagInit and platform.system() == 'Windows': - try: - matchRes = re.match( - r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请前往该地址验证\s+->\s+(http[s]{0,1}://captcha\.go-cqhttp\.org/captcha\?[^\s]+).*$', - res_data_raw - ) - if matchRes != None: - matchResList = list(matchRes.groups()) - if len(matchResList) == 1: - matchResUrl = matchResList[0] - self.show_url_webbrowser(matchResUrl) - except: - pass + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) def stop(self): self.exit() self.UIObject['root'].destroy() def exit(self): - self.root.UIObject['root_walleq_terminal'].pop(self.bot.hash) - + self.root.UIObject['root_opqbot_terminal'].pop(self.bot.hash) -class CWCBTerminalUI(object): +class napcatTerminalUI(object): def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=None): self.Model_name = Model_name self.root = root @@ -1371,14 +2310,15 @@ def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None, bot=No def start(self): self.UIObject['root'] = tkinter.Toplevel() - self.UIObject['root'].title('ComWeChatBotClient 终端 - %s' % str(self.bot.id)) + self.UIObject['root'].title('NapCat 终端 - %s' % str(self.bot.id)) self.UIObject['root'].geometry('800x600') self.UIObject['root'].minsize(800, 600) self.UIObject['root'].grid_rowconfigure(0, weight=15) self.UIObject['root'].grid_rowconfigure(1, weight=0) self.UIObject['root'].grid_columnconfigure(0, weight=0) self.UIObject['root'].grid_columnconfigure(1, weight=2) - self.UIObject['root'].grid_columnconfigure(2, weight=0) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) self.UIObject['root'].resizable( width=True, height=True @@ -1403,7 +2343,7 @@ def start(self): column=0, sticky="nsew", rowspan=1, - columnspan=2, + columnspan=3, padx=(15, 0), pady=(15, 0), ipadx=0, @@ -1422,7 +2362,7 @@ def start(self): # ) self.UIObject['tree_yscroll'].grid( row=0, - column=2, + column=3, sticky="nsw", rowspan=1, columnspan=1, @@ -1431,8 +2371,9 @@ def start(self): ipadx=0, ipady=0 ) + self.UIData['flag_tree_is_bottom'] = True self.UIObject['tree'].configure( - yscrollcommand=self.UIObject['tree_yscroll'].set + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) ) self.root_Entry_init( @@ -1450,15 +2391,37 @@ def start(self): self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) self.UIObject['root_input'].grid( row=1, - column=1, + column=0, sticky="s", rowspan=1, - columnspan=3, - padx=(15, 15), + columnspan=2, + padx=(15, 0), pady=(8, 15), ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, ipady=0 ) + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) @@ -1468,6 +2431,15 @@ def start(self): self.exit() + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + def tree_rightKey(self, event): # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 # iid = self.UIObject['tree'].identify_row(event.y) @@ -1491,8 +2463,8 @@ def rightKey_action(self, action: str): self.UIObject['root'].update() def root_Entry_enter_Func(self, name): - def resFunc(event): - self.root_Entry_enter(name, event) + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) return resFunc @@ -1500,7 +2472,7 @@ def root_Entry_enter(self, name, event): if name == 'root_input': input = self.UIData['root_input_StringVar'].get() if len(input) >= 0 and len(input) < 1000: - self.root.setCWCBModelSend(self.bot.hash, input) + self.root.setNapCatModelSend(self.bot.hash, input) self.UIData['root_input_StringVar'].set('') def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', @@ -1522,7 +2494,8 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he self.UIData[str_name] = tkinter.StringVar() self.UIObject[obj_name] = tkinter.Entry( self.UIObject[obj_root], - textvariable=self.UIData[str_name] + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') ) self.UIObject[obj_name].configure( bg=self.UIConfig['color_004'], @@ -1543,17 +2516,9 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he # height = height # ) - def show_url_webbrowser(self, url): - res = tkinter.messagebox.askquestion("请完成验证", "是否通过浏览器访问 \"" + url + "\" ?") - try: - if res == 'yes': - webbrowser.open(url) - except webbrowser.Error as error_info: - tkinter.messagebox.showerror("webbrowser.Error", error_info) - def tree_init_line(self): - if self.bot.hash in self.root.UIObject['root_cwcb_terminal_data']: - for line in self.root.UIObject['root_cwcb_terminal_data'][self.bot.hash]: + if self.bot.hash in self.root.UIObject['root_napcat_terminal_data']: + for line in self.root.UIObject['root_napcat_terminal_data'][self.bot.hash]: self.tree_add_line(line, flagInit = True) def tree_add_line(self, data, flagInit = False): @@ -1572,32 +2537,42 @@ def tree_add_line(self, data, flagInit = False): res_data ) ) - self.UIObject['tree'].see(iid) - self.UIObject['tree'].update() + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass - if not flagInit and platform.system() == 'Windows': - try: - matchRes = re.match( - r'^\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\]\s\[WARNING\]:\s请前往该地址验证\s+->\s+(http[s]{0,1}://captcha\.go-cqhttp\.org/captcha\?[^\s]+).*$', - res_data_raw - ) - if matchRes != None: - matchResList = list(matchRes.groups()) - if len(matchResList) == 1: - matchResUrl = matchResList[0] - self.show_url_webbrowser(matchResUrl) - except: - pass + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + def stop(self): self.exit() self.UIObject['root'].destroy() def exit(self): - self.root.UIObject['root_cwcb_terminal'].pop(self.bot.hash) - + self.root.UIObject['root_napcat_terminal'].pop(self.bot.hash) @@ -1614,7 +2589,7 @@ def __init__(self, Model_name, logger_proc=None, root=None, root_tk=None): def start(self): self.UIObject['root'] = tkinter.Toplevel() - self.UIObject['root'].title('OlivOS 终端') + self.UIObject['root'].title('OlivOS 终端 - %s' % OlivOS.infoAPI.OlivOS_Version_Title) self.UIObject['root'].geometry('900x600') self.UIObject['root'].minsize(900, 600) self.UIObject['root'].grid_rowconfigure(0, weight=15) @@ -1688,8 +2663,9 @@ def start(self): ipadx=0, ipady=0 ) + self.UIData['flag_tree_is_bottom'] = True self.UIObject['tree'].configure( - yscrollcommand=self.UIObject['tree_yscroll'].set + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) ) self.tree_edit_UI_Combobox_init( @@ -1746,7 +2722,7 @@ def start(self): padx=(0, 15), pady=(8, 15), ipadx=0, - ipady=0 + ipady=2 ) self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') @@ -1758,6 +2734,15 @@ def start(self): self.exit() + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + def tree_rightKey(self, event): # 右键设置的选择在后续流程中未生效,不知为何,等后续解决 # iid = self.UIObject['tree'].identify_row(event.y) @@ -1912,8 +2897,9 @@ def tree_add_line(self, data): ), tag=log_level ) - self.UIObject['tree'].see(iid) - self.UIObject['tree'].update() + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass @@ -2298,7 +3284,8 @@ def start(self): self.UIObject['root'].grid_rowconfigure(1, weight=0) self.UIObject['root'].grid_columnconfigure(0, weight=0) self.UIObject['root'].grid_columnconfigure(1, weight=2) - self.UIObject['root'].grid_columnconfigure(2, weight=0) + self.UIObject['root'].grid_columnconfigure(2, weight=2) + self.UIObject['root'].grid_columnconfigure(3, weight=0) self.UIObject['root'].resizable( width=True, height=True @@ -2321,7 +3308,7 @@ def start(self): column=0, sticky="nsew", rowspan=1, - columnspan=2, + columnspan=3, padx=(15, 0), pady=(15, 0), ipadx=0, @@ -2334,7 +3321,7 @@ def start(self): ) self.UIObject['tree_yscroll'].grid( row=0, - column=2, + column=3, sticky="nsw", rowspan=1, columnspan=1, @@ -2343,8 +3330,9 @@ def start(self): ipadx=0, ipady=0 ) + self.UIData['flag_tree_is_bottom'] = True self.UIObject['tree'].configure( - yscrollcommand=self.UIObject['tree_yscroll'].set + yscrollcommand=self.scroll_onChange(self.UIObject['tree_yscroll'].set) ) self.root_Entry_init( @@ -2360,18 +3348,39 @@ def start(self): title='输入' ) self.UIObject['root_input'].bind("", self.root_Entry_enter_Func('root_input')) - self.UIObject['root_input'].grid( row=1, - column=1, + column=0, sticky="s", rowspan=1, - columnspan=3, - padx=(15, 15), + columnspan=2, + padx=(15, 0), pady=(8, 15), ipadx=0, + ipady=4 + ) + + self.root_Button_init( + name='root_button_save', + text='>', + command=self.root_Entry_enter_Func('root_input'), + x=800 - 15 * 2 - 5, + y=600 - 15 * 1 - 24, + width=16, + height=1 + ) + self.UIObject['root_button_save'].grid( + row=1, + column=2, + sticky="swe", + rowspan=1, + columnspan=2, + padx=(0, 15), + pady=(8, 15), + ipadx=8, ipady=0 ) + self.UIObject['root'].iconbitmap('./resource/tmp_favoricon.ico') self.UIObject['root'].protocol("WM_DELETE_WINDOW", self.stop) @@ -2381,6 +3390,15 @@ def start(self): self.exit() + def scroll_onChange(self, command): + def res(*arg, **kwarg): + if arg[1] == '1.0': + self.UIData['flag_tree_is_bottom'] = True + else: + self.UIData['flag_tree_is_bottom'] = False + return command(*arg, **kwarg) + return res + def tree_rightKey(self, event): self.UIObject['tree_rightkey_menu'].delete(0, tkinter.END) self.UIObject['tree_rightkey_menu'].add_command(label='查看', command=lambda: self.rightKey_action('show')) @@ -2420,17 +3438,16 @@ def root_AccountEdit_init(self): self.UIObject['root_terminal_account_edit'].start() def root_Entry_enter_Func(self, name): - def resFunc(event): - self.root_Entry_enter(name, event) + def resFunc(*arg, **kwarg): + self.root_Entry_enter(name, None) return resFunc def root_Entry_enter(self, name, event): if name == 'root_input': input = self.UIData['root_input_StringVar'].get() - if len(input) >= 0 and len(input) < 1000: + if len(input) > 0 and len(input) < 1000: self.root.setVirtualModelSend(self.bot.hash, input, self.user_conf_data) - pass self.UIData['root_input_StringVar'].set('') def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, height, action, title='', @@ -2446,7 +3463,8 @@ def root_Entry_init(self, obj_root, obj_name, str_name, x, y, width_t, width, he self.UIData[str_name] = tkinter.StringVar() self.UIObject[obj_name] = tkinter.Entry( self.UIObject[obj_root], - textvariable=self.UIData[str_name] + textvariable=self.UIData[str_name], + font=('TkDefaultFont 12') ) self.UIObject[obj_name].configure( bg=self.UIConfig['color_004'], @@ -2492,11 +3510,35 @@ def tree_add_line(self, data, user_conf=None): res_data_list_this ) ) - self.UIObject['tree'].see(iid) - self.UIObject['tree'].update() + if self.UIData['flag_tree_is_bottom']: + self.UIObject['tree'].see(iid) + #self.UIObject['tree'].update() except: pass + def buttom_action(self, name, action): + if name in self.UIObject: + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_006']) + if action == '': + self.UIObject[name].configure(bg=self.UIConfig['color_003']) + + def root_Button_init(self, name, text, command, x, y, width, height): + self.UIObject[name] = tkinter.Button( + self.UIObject['root'], + text=text, + command=command, + bd=0, + activebackground=self.UIConfig['color_002'], + activeforeground=self.UIConfig['color_001'], + bg=self.UIConfig['color_003'], + fg=self.UIConfig['color_004'], + relief='groove', + height=height + ) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + self.UIObject[name].bind('', lambda x: self.buttom_action(name, '')) + def stop(self): self.exit() self.UIObject['root'].destroy() @@ -2580,6 +3622,15 @@ def getMenu(self, data): ) ) ) + elif item_this[3] == 'opqbot': + list_new.append( + pystray.MenuItem( + item_this[0], + self.root.startOPQBotTerminalUISendFunc( + item_this[1] + ) + ) + ) elif item_this[3] == 'virtual_terminal': list_new.append( pystray.MenuItem( @@ -2589,6 +3640,15 @@ def getMenu(self, data): ) ) ) + elif item_this[3] == 'napcat': + list_new.append( + pystray.MenuItem( + item_this[0], + self.root.startNapCatTerminalUISendFunc( + item_this[1] + ) + ) + ) if len(list_new) > 0: return pystray.Menu(*list_new) else: diff --git a/OlivOS/webviewUIAPI.py b/OlivOS/nativeGUI/webviewUIAPI.py similarity index 100% rename from OlivOS/webviewUIAPI.py rename to OlivOS/nativeGUI/webviewUIAPI.py diff --git a/README.md b/README.md index 2a3c22b9..6f779dfd 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ [![OnebotV11](https://img.shields.io/badge/-OnebotV11-111111?style=flat-square&logoColor=white)](https://github.com/botuniverse/onebot) [![OnebotV12](https://img.shields.io/badge/-OnebotV12-111111?style=flat-square&logoColor=white)](https://github.com/botuniverse/onebot) [![RED](https://img.shields.io/badge/-RED-111111?style=flat-square&logoColor=white)](https://chrononeko.github.io/QQNTRedProtocol/) +[![QQ](https://img.shields.io/badge/-QQ-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://im.qq.com/index/) +[![NapCat](https://img.shields.io/badge/-NapCat-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://github.com/NapNeko/NapCatQQ) +[![Go-CQHttp](https://img.shields.io/badge/-GoCQHttp-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://github.com/Mrs4s/go-cqhttp) +[![Walle-Q](https://img.shields.io/badge/-WalleQ-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://github.com/onebot-walle/walle-q) +[![WeChat](https://img.shields.io/badge/-WeChat-07C160?style=flat-square&logo=wechat&logoColor=white)](https://weixin.qq.com/) +[![ComWeChatBotClient](https://img.shields.io/badge/-ComWeChatBotClient-07C160?style=flat-square&logo=wechat&logoColor=white)](https://github.com/JustUndertaker/ComWeChatBotClient) [![QQ全域](https://img.shields.io/badge/-QQ%E5%85%A8%E5%9F%9F-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://bot.q.qq.com/wiki/) [![QQ频道](https://img.shields.io/badge/-QQ%E9%A2%91%E9%81%93-EB1923?style=flat-square&logo=Tencent%20QQ&logoColor=white)](https://bot.q.qq.com/wiki/) [![DingTalk](https://img.shields.io/badge/-%E9%92%89%E9%92%89-007FFF?style=flat-square&logo=alibabadotcom&logoColor=white)](https://open.dingtalk.com/) @@ -64,6 +70,10 @@ ## 方针 技术路线上,基于Python、采用模块化框架、进程分离、消息队列通信、前后端分离、响应式布局、渐进式渲染的基本思路,实现一个高并发性、高可靠性、高适应性的整体方案。 +## 欢迎点击Star,这对我们将带来很大的支持! +[![Star History Chart](https://api.star-history.com/svg?repos=OlivOS-Team/OlivOS&type=Date)](https://star-history.com/#OlivOS-Team/OlivOS&Date) + + ## 开始使用 ### 发布版本 #### Windows @@ -125,7 +135,7 @@ if __name__ == '__main__': You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -`OlivOS` 采用 `AGPLv3` 协议开源。为了整个社区的良性发展,我们**强烈建议**您做到以下几点: +`OlivOS` 采用 `AGPLv3` 协议开源。为了整个社区的良性发展,我们**强烈建议**您做到以下几点(此声明继承自[Mirai](https://github.com/mamoe/mirai)): - **间接接触(包括但不限于使用 `Http API` 或 跨进程技术)到 `OlivOS` 的软件使用 `AGPLv3` 开源** @@ -136,3 +146,10 @@ if __name__ == '__main__': - 若引用 OlivOS 发布的软件包而不修改 OlivOS,则衍生项目需在描述的任意部位提及使用 OlivOS。 - 若修改 OlivOS 源代码再发布,**或参考 OlivOS 内部实现发布另一个项目**,则衍生项目必须在**文章首部**或 'OlivOS' 相关内容**首次出现**的位置**明确声明**来源于本仓库 (`https://github.com/OlivOS-Team/OlivOS`)。不得扭曲或隐藏免费且开源的事实。 + +## 特别声明 +有[CSDN旗下GitCode被曝批量搬运Github项目](https://tech.ifeng.com/c/8ako6om7AC7)的珠玉在前,本项目特别在此声明,本项目的官方源代码仅在以下平台发布: + ++ [Github - OlivOS-Team/OlivOS](https://github.com/OlivOS-Team/OlivOS) + +任何未经本仓库所属的组织允许的超出以上代码托管平台的冒充行为,都是未授权的,请警惕。 diff --git a/conf/basic_default.json b/conf/basic_default.json index 7d4f964e..dd4011fd 100644 --- a/conf/basic_default.json +++ b/conf/basic_default.json @@ -12,16 +12,22 @@ "OlivOS_gocqhttp_lib_exe_model", "OlivOS_walleq_lib_exe_model", "OlivOS_cwcb_lib_exe_model", + "OlivOS_opqbot_lib_exe_model", "OlivOS_hackChat_link", + "OlivOS_OPQBot_link", + "OlivOS_qqRed_link", + "OlivOS_dingtalk_link", "OlivOS_plugin", "OlivOS_virtual_terminal_link", "OlivOS_flask_post_rx", "OlivOS_onebotV12_link", "OlivOS_qqGuild_link", + "OlivOS_qqGuildv2_link", "OlivOS_discord_link", "OlivOS_telegram_poll", "OlivOS_fanbook_poll", "OlivOS_kaiheila_link", + "OlivOS_mhyVila_link", "OlivOS_dodo_link", "OlivOS_biliLive_link", "OlivOS_update_check" @@ -51,15 +57,21 @@ "OlivOS_gocqhttp_lib_exe_model", "OlivOS_walleq_lib_exe_model", "OlivOS_cwcb_lib_exe_model", + "OlivOS_opqbot_lib_exe_model", "OlivOS_hackChat_link", + "OlivOS_OPQBot_link", + "OlivOS_qqRed_link", + "OlivOS_dingtalk_link", "OlivOS_virtual_terminal_link", "OlivOS_flask_post_rx", "OlivOS_onebotV12_link", "OlivOS_qqGuild_link", + "OlivOS_qqGuildv2_link", "OlivOS_discord_link", "OlivOS_telegram_poll", "OlivOS_fanbook_poll", "OlivOS_kaiheila_link", + "OlivOS_mhyVila_link", "OlivOS_dodo_link", "OlivOS_biliLive_link" ] @@ -69,15 +81,21 @@ "gocqhttp_lib_exe_model", "walleq_lib_exe_model", "cwcb_lib_exe_model", + "opqbot_lib_exe_model", "hackChat_link", + "OPQBot_link", + "qqRed_link", + "dingtalk_link", "terminal_link", "flask_post_rx", "onebotV12_link", "qqGuild_link", + "qqGuildv2_link", "discord_link", "telegram_poll", "fanbook_poll", "kaiheila_link", + "mhyVila_link", "dodo_link", "biliLive_link" ] @@ -95,10 +113,14 @@ "OlivOS_gocqhttp_lib_rx_queue", "OlivOS_walleq_lib_rx_queue", "OlivOS_cwcb_lib_rx_queue", + "OlivOS_opqbot_lib_rx_queue", "OlivOS_virtual_terminal_queue", "OlivOS_hackChat_queue", + "OlivOS_OPQBot_queue", "OlivOS_biliLive_queue", - "OlivOS_onebotv12_queue" + "OlivOS_onebotv12_queue", + "OlivOS_qqRed_queue", + "OlivOS_dingtalk_queue" ], "models": { "OlivOS_sleep": { @@ -262,6 +284,17 @@ "logger_proc": "OlivOS_logger", "debug": false }, + "OlivOS_qqGuildv2_link": { + "enable": true, + "name": "OlivOS_qqGuildv2_link", + "type": "qqGuildv2_link", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": null, + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": false + }, "OlivOS_discord_link": { "enable": true, "name": "OlivOS_discord_link", @@ -284,6 +317,28 @@ "logger_proc": "OlivOS_logger", "debug": false }, + "OlivOS_OPQBot_link": { + "enable": true, + "name": "OlivOS_OPQBot_link", + "type": "OPQBot_link", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_OPQBot_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": false + }, + "OlivOS_qqRed_link": { + "enable": true, + "name": "OlivOS_qqRed_link", + "type": "qqRed_link", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_qqRed_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": false + }, "OlivOS_biliLive_link": { "enable": true, "name": "OlivOS_biliLive_link", @@ -306,6 +361,16 @@ "logger_proc": "OlivOS_logger", "debug": false }, + "OlivOS_mhyVila_link": { + "enable": true, + "name": "OlivOS_mhyVila_link", + "type": "mhyVila_link", + "interval": 0.2, + "dead_interval": 1, + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": false + }, "OlivOS_telegram_poll": { "enable": true, "name": "OlivOS_telegram_poll", @@ -368,6 +433,17 @@ "logger_proc": "OlivOS_logger", "debug": false }, + "OlivOS_dingtalk_link": { + "enable": true, + "name": "OlivOS_dingtalk_link", + "type": "dingtalk_link", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_dingtalk_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "debug": false + }, "OlivOS_gocqhttp_lib_exe_model": { "enable": true, "name": "OlivOS_gocqhttp_lib_exe_model", @@ -407,6 +483,19 @@ "control_queue": "OlivOS_control_queue", "debug": false }, + "OlivOS_opqbot_lib_exe_model": { + "enable": true, + "name": "OlivOS_opqbot_lib_exe_model", + "type": "opqbot_lib_exe_model", + "interval": 0.2, + "dead_interval": 1, + "rx_queue": "OlivOS_opqbot_lib_rx_queue", + "tx_queue": "OlivOS_rx_queue", + "logger_proc": "OlivOS_logger", + "target_proc": null, + "control_queue": "OlivOS_control_queue", + "debug": false + }, "OlivOS_webview_page": { "enable": true, "name": "OlivOS_webview_page", diff --git a/hook/hook-email.py b/hook/hook-email.py new file mode 100644 index 00000000..2427ea56 --- /dev/null +++ b/hook/hook-email.py @@ -0,0 +1,9 @@ +from PyInstaller.utils.hooks import collect_all + +def hook(hook_api): + packages = ['email'] + for package in packages: + datas, binaries, hiddenimports = collect_all(package) + #hook_api.add_datas(datas) + #hook_api.add_binaries(binaries) + hook_api.add_imports(*hiddenimports) diff --git a/requirements310.txt b/requirements310.txt index 26106166..92802a32 100644 --- a/requirements310.txt +++ b/requirements310.txt @@ -1,4 +1,4 @@ -pyinstaller +pyinstaller==6.9.0 flask Werkzeug==2.2.2 gevent diff --git a/requirements310_pure.txt b/requirements310_pure.txt index 83096c9f..3c30260a 100644 --- a/requirements310_pure.txt +++ b/requirements310_pure.txt @@ -1,4 +1,4 @@ -pyinstaller +pyinstaller==6.9.0 flask Werkzeug==2.2.2 gevent diff --git a/requirements310_win.txt b/requirements310_win.txt index 7d4e97a9..ff12155d 100644 --- a/requirements310_win.txt +++ b/requirements310_win.txt @@ -1,4 +1,4 @@ -pyinstaller +pyinstaller==6.9.0 flask Werkzeug==2.2.2 gevent diff --git a/setup.py b/setup.py index 9e113c65..7282861b 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ long_description = f.read() setuptools.setup(name='olivos', - version='0.11.25', + version='0.11.48', description='OlivOS - Witness Union', long_description=long_description, long_description_content_type='text/markdown',