diff --git a/README.md b/README.md index 83e3848..c505bf6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Star Yuuki(pYthon) BOT - Yuuki ================== ![Version](https://img.shields.io/badge/v6.5.3-OpenSource-FF0033.svg) ![Series](https://img.shields.io/badge/syb-Series-7700FF.svg) ![License](https://img.shields.io/badge/license-MPL--2.0-FF6600.svg) ![Python](https://img.shields.io/badge/python-3.x-0066FF.svg) ![Platform](https://img.shields.io/badge/base_on-LINE-00DD00.svg) -![Scrutinizer-CI_Build](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/build.png?b=master) ![Scrutinizer-CI_Quality](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/quality-score.png?b=master) ![Code-Inspector_Score](https://www.code-inspector.com/project/3632/score/svg) ![Code-Inspector_Score](https://www.code-inspector.com/project/3632/status/svg) +[![Scrutinizer-CI_Build](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/build.png?b=master) ![Scrutinizer-CI_Quality](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/star-inc/star_yuuki_bot/) [![Code-Inspector_Score](https://www.code-inspector.com/project/3632/score/svg) ![Code-Inspector_Score](https://www.code-inspector.com/project/3632/status/svg)](https://frontend.code-inspector.com/public/project/3632/star_yuuki_bot/dashboard) A Python BOT for LINE Group Protection. ![ICON](logo.png) diff --git a/libs/__init__.py b/libs/__init__.py index 365e21f..5f7ce8c 100644 --- a/libs/__init__.py +++ b/libs/__init__.py @@ -6,7 +6,7 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ -from .yuuki import Yuuki from .config import Yuuki_Config +from .yuuki import Yuuki __all__ = ['connection', 'data', 'data_mds', 'thread_control', 'Yuuki', 'Yuuki_Config'] diff --git a/libs/config.py b/libs/config.py index b419019..5160bc0 100644 --- a/libs/config.py +++ b/libs/config.py @@ -6,11 +6,22 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ +import os + import yaml class Yuuki_Config: - """ Configure Yuuki """ + """ + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! DO NOT TOUCH DEFAULT SETTINGS !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + Please config the value you want to set though `config.yaml`. + It will overwrite these settings. + + """ connectInfo = { "Host": "", @@ -26,7 +37,7 @@ class Yuuki_Config: systemConfig = { "name": "Yuuki", - "version": "v6.5.3-alpha", + "version": "v6.5.3-alpha_RC1", "version_check": True, "project_url": "https://tinyurl.com/syb-yuuki", "man_page": "https://tinyurl.com/yuuki-manual", @@ -36,6 +47,7 @@ class Yuuki_Config: "Seq": 0, "Admin": [], "Advanced": False, + "WebAdmin": False, # Advanced Mode Required "SecurityService": False, "Hour_KickLimit": 10, "Hour_CancelLimit": 10, @@ -45,11 +57,13 @@ class Yuuki_Config: } def __init__(self, config_path="config.yaml"): + assert os.path.isfile(config_path), "The configure file, `config.yaml` was not found." with open(config_path, "r") as configfile: self.config = yaml.load(configfile, Loader=yaml.FullLoader) self._yuuki_config() def _yuuki_config(self): + assert self.config is not None, "Invalid configure file" if "Yuuki" in self.config: for key in self.config["Yuuki"]: if key in self.systemConfig: @@ -67,7 +81,7 @@ class Yuuki_Config: if "Account" in self.config.get("LINE"): for key in self.config["LINE"]["Account"]: if key in ["X-Line-Application", "User-Agent"]: - self.config["LINE"]["Account"][key] = self.config["LINE"]["Account"][key].replace("\\t","\t") + self.config["LINE"]["Account"][key] = self.config["LINE"]["Account"][key].replace("\\t", "\t") if key in self.connectHeader: self.connectHeader[key] = self.config["LINE"]["Account"][key] diff --git a/libs/connection.py b/libs/connection.py index 809b8ac..e13a459 100644 --- a/libs/connection.py +++ b/libs/connection.py @@ -11,12 +11,11 @@ from thrift.transport import THttpClient from yuuki_core.TalkService import Client -""" NC HightSpeed Lib """ +# NC HighSpeed Library try: from thrift.protocol import fastbinary except ImportError: print("[No fast_binary using]") -########################################## class Yuuki_Connect: diff --git a/libs/data.py b/libs/data.py index 8884140..de986be 100644 --- a/libs/data.py +++ b/libs/data.py @@ -11,16 +11,15 @@ import os import random import time -import requests +from tornado.httpclient import HTTPClient, HTTPRequest from yuuki_core.ttypes import OpType -from .data_mds import listen as msd_listen +from .data_mds import PythonMDS from .thread_control import Yuuki_Multiprocess from .thread_control import Yuuki_Thread class Yuuki_Data: - # Data Struct Define Data = {} @@ -80,49 +79,42 @@ class Yuuki_Data: if not os.path.isdir(self.DataPath): os.mkdir(self.DataPath) - for Type in self.DataType: - name = self.DataPath + self.DataName.format(Type) + for data_type in self.DataType: + name = self.DataPath + self.DataName.format(data_type) + + test_result = 0 if not os.path.isfile(name): with open(name, "w") as f: f.write("") - Type = 0 else: with open(name, "r") as f: try: json.load(f) - Type = 0 except ValueError: - Type = 1 - assert Type == 0, "{}\nJson Test Error".format(name) + test_result = 1 + assert test_result == 0, "{}\nJson Test Error".format(name) - for Type in self.DataType: - name = self.DataPath + self.DataName.format(Type) with open(name, "r+") as f: text = f.read() if text != "": - self.Data[Type] = json.loads(text) + self.Data[data_type] = json.loads(text) else: - self.Data[Type] = self.DataType[Type] - f.write(json.dumps(self.Data[Type])) + self.Data[data_type] = self.DataType[data_type] + f.write(json.dumps(self.Data[data_type])) + return self._MDS_Initialize() def _MDS_Initialize(self): if self.threading: + mds = PythonMDS() self.mdsHost = "http://localhost:2019/" self.mdsCode = "{}.{}".format(random.random(), time.time()) - self.MdsThreadControl.add(msd_listen, (self.mdsCode,)) + self.MdsThreadControl.add(mds.mds_listen, (self.mdsCode,)) # MDS Sync time.sleep(1) - requests.post( - url=self.mdsHost, - json={ - "code": self.mdsCode, - "do": "SYC", - "path": self.Data - } - ) + self.mdsShake("SYC", self.Data) return self._Log_Initialize() def _Log_Initialize(self): @@ -144,22 +136,28 @@ class Yuuki_Data: Function(*args) def mdsShake(self, do, path, data=None): + status = 0 if self.threading: - mds = requests.post( + http_client = HTTPClient() + http_request = HTTPRequest( url=self.mdsHost, - json={ + method="POST", + body=json.dumps({ "code": self.mdsCode, "do": do, "path": path, "data": data - } + }) ) - over = mds.json() - assert_result = "mds - ERROR\n{} on {}".format(do, path) - assert over["status"] == 200, assert_result + response = http_client.fetch(http_request) + over = json.loads(response.body) + if "status" in over: + status = over["status"] + assert_result = "mds - ERROR {}\n{} on {}".format(status, do, path) + assert status == 200, assert_result return over else: - status = {"status": 0} + status = {"status": status} return json.dumps(status) def _local_query(self, query_data): diff --git a/libs/data_mds.py b/libs/data_mds.py index 65107a1..df6708f 100644 --- a/libs/data_mds.py +++ b/libs/data_mds.py @@ -10,54 +10,67 @@ # Initializing import json +import types +from abc import ABC from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler -switch_data = {} +# Works +_work = {} auth_code = 0 -# Functions -def mds_exit(data): - if data: - exit(0) +class IndexHandler(RequestHandler, ABC): + def get(self): + self.write(''' + Python MDS Server
+ To switch data in multiprocessing.
+ (c)2020 Star Inc. + ''') + + def post(self): + global auth_code + req_body = self.request.body + req_str = req_body.decode('utf8') + req_res = json.loads(req_str) + if req_res.get("code") == auth_code: + result = _work[req_res.get("do")]( + { + "path": req_res.get("path"), + "data": req_res.get("data") + } + ) + else: + result = {"status": 401} + if isinstance(result, types.GeneratorType): + result = {"status": 200} + self.write(json.dumps(result)) -def update(data): - global switch_data - # noinspection PyBroadException - try: - if type(data["path"]) is list: - over = query({"path": data["path"]}) - over.get("data").update(data["data"]) - return {"status": 200} - return {"status": 400} - except: - return {"status": 500} +class PythonMDS: + switch_data = {} + # Main + app = Application([ + ('/', IndexHandler) + ]) + server = HTTPServer(app) + async_lock = IOLoop.current() -def delete(data): - global switch_data - # noinspection PyBroadException - try: - if type(data["path"]) is list: - over = query({"path": data["path"]}) - over.get("data").pop(data["data"]) - return {"status": 200} - return {"status": 400} - except: - return {"status": 500} + def __init__(self): + _work["UPT"] = self._update + _work["DEL"] = self._delete + _work["GET"] = self._query + _work["SYC"] = self._sync + _work["YLD"] = self._yuuki_limit_decrease + _work["EXT"] = self._shutdown - -def query(data): - global switch_data - query_data = data["path"] - # noinspection PyBroadException - try: - if type(switch_data) is dict and type(query_data) is list: - result = switch_data + def _query(self, data): + query_data = data["path"] + if type(self.switch_data) is dict and type(query_data) is list: + result = self.switch_data query_len = len(query_data) - 1 for count, key in enumerate(query_data): if key in result: @@ -72,73 +85,39 @@ def query(data): return {"status": 200, "data": result} return {"status": 400} - except: - return {"status": 500} + def _update(self, data): + if type(data["path"]) is list: + over = self._query({"path": data["path"]}) + over.get("data").update(data["data"]) + return {"status": 200} + return {"status": 400} -def sync(data): - global switch_data - # noinspection PyBroadException - try: - switch_data = data["path"] + def _delete(self, data): + if type(data["path"]) is list: + over = self._query({"path": data["path"]}) + over.get("data").pop(data["data"]) + return {"status": 200} + return {"status": 400} + + def _sync(self, data): + self.switch_data = data["path"] return {"status": 200} - except: - return {"status": 500} - -def yuukiLimitDecrease(data): - global switch_data - # noinspection PyBroadException - try: - switch_data["LimitInfo"][data["path"]][data["userId"]] -= 1 + def _yuuki_limit_decrease(self, data): + self.switch_data["LimitInfo"][data["path"]][data["userId"]] -= 1 return {"status": 200} - except: - return {"status": 500} + def _shutdown(self, data): + if data: + pass + self.server.stop() + yield True + self.async_lock.stop() + self.async_lock.close() -# Works -_work = { - "EXT": mds_exit, - "UPT": update, - "DEL": delete, - "GET": query, - "SYC": sync, - "YLD": yuukiLimitDecrease -} - - -class IndexHandler(RequestHandler): - def get(self): - self.write(''' - Python MDS Server
- To switch data in multiprocessing.
- (c)2019 Star Inc. - ''') - - def post(self): + def mds_listen(self, code): global auth_code - req_body = self.request.body - req_str = req_body.decode('utf8') - req_res = json.loads(req_str) - if req_res.get("code") == auth_code: - result = _work[req_res.get("do")]( - { - "path":req_res.get("path"), - "data":req_res.get("data") - } - ) - else: - result = {"status": 401} - if not result: - result = {"status": 500} - self.write(json.dumps(result)) - - -# Main -def listen(code): - global auth_code - auth_code = code - app = Application([('/', IndexHandler)]) - server = HTTPServer(app) - server.listen(2019) - IOLoop.current().start() + auth_code = code + self.server.listen(2019) + self.async_lock.start() diff --git a/libs/events/__init__.py b/libs/events/__init__.py index f7574ec..8787d02 100644 --- a/libs/events/__init__.py +++ b/libs/events/__init__.py @@ -7,8 +7,9 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ +from .callback import Yuuki_Callback from .command import Yuuki_Command from .join_group import Yuuki_JoinGroup from .security import Yuuki_Security -__all__ = ["Yuuki_Command", "Yuuki_JoinGroup", "Yuuki_Security"] +__all__ = ["Yuuki_Command", "Yuuki_JoinGroup", "Yuuki_Security", "Yuuki_Callback"] diff --git a/libs/events/callback.py b/libs/events/callback.py new file mode 100644 index 0000000..4c89a70 --- /dev/null +++ b/libs/events/callback.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" +Yuuki_Libs +(c) 2020 Star Inc. +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. +""" +import time + +from yuuki_core.ttypes import ContentType + +from ..tools import Yuuki_StaticTools, Yuuki_DynamicTools + + +class Yuuki_Callback: + def __init__(self, Yuuki): + """ + Event Type: + SEND_MESSAGE(25) + """ + self.Yuuki = Yuuki + + self.Yuuki_StaticTools = Yuuki_StaticTools() + self.Yuuki_DynamicTools = Yuuki_DynamicTools(self.Yuuki) + + def _shutdown(self, ncMessage): + self.Yuuki.Thread_Control.add(self._shutdown_reply, (ncMessage,)) + self.Yuuki.exit() + + def _shutdown_reply(self, ncMessage): + time.sleep(1) + self.Yuuki_DynamicTools.sendText( + ncMessage.message.to, + self.Yuuki.get_text("Exit.") + ) + + def _text(self, ncMessage): + actions = { + '[Yuuki] Remote Shutdown': self._shutdown, + } + if ncMessage.message.text in actions: + actions[ncMessage.message.text](ncMessage) + + def action(self, ncMessage): + if ncMessage.message.contentType == ContentType.NONE: + self._text(ncMessage) diff --git a/libs/events/command.py b/libs/events/command.py index 464f1cf..9a47787 100644 --- a/libs/events/command.py +++ b/libs/events/command.py @@ -275,9 +275,7 @@ class Yuuki_Command: if ncMessage.message.toType == MIDType.GROUP: GroupInfo = self.Yuuki_DynamicTools.getClient( self.Yuuki.MyMID).getGroup(ncMessage.message.to) - GroupPrivilege = self.Yuuki.Admin + \ - [self.Yuuki_StaticTools.sybGetGroupCreator( - GroupInfo).mid] + GroupPrivilege = self.Yuuki.Admin + [self.Yuuki_StaticTools.sybGetGroupCreator(GroupInfo).mid] if ncMessage.message.from_ in GroupPrivilege: self.Yuuki_DynamicTools.sendText(self.Yuuki_StaticTools.sendToWho( ncMessage), self.Yuuki.get_text("Bye Bye")) @@ -296,11 +294,13 @@ class Yuuki_Command: def _Exit(self, ncMessage): if ncMessage.message.from_ in self.Yuuki.Admin: - self.Yuuki_DynamicTools.sendText(self.Yuuki_StaticTools.sendToWho( - ncMessage), self.Yuuki.get_text("Exit.")) + self.Yuuki_DynamicTools.sendText( + self.Yuuki_StaticTools.sendToWho(ncMessage), + self.Yuuki.get_text("Exit.") + ) self.Yuuki.exit() - def _Com(self, ncMessage): + def _SystemCall(self, ncMessage): msgSep = ncMessage.message.text.split(" ") if ncMessage.message.from_ in self.Yuuki.Admin: # noinspection PyBroadException @@ -330,7 +330,7 @@ class Yuuki_Command: 'GroupBackup': self._GroupBackup, 'Quit': self._Quit, 'Exit': self._Exit, - 'Com': self._Com, + 'SystemCall': self._SystemCall, } if Yuuki_Name == msgSep[0] and msgSep[1] in actions: actions[msgSep[1]](ncMessage) diff --git a/libs/events/security.py b/libs/events/security.py index 51991ac..4ea461e 100644 --- a/libs/events/security.py +++ b/libs/events/security.py @@ -7,10 +7,10 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ -from ..tools import Yuuki_StaticTools, Yuuki_DynamicTools - from yuuki_core.ttypes import OpType +from ..tools import Yuuki_StaticTools, Yuuki_DynamicTools + class Yuuki_Security: def __init__(self, Yuuki): @@ -268,11 +268,11 @@ class Yuuki_Security: SecurityInfo = self.Yuuki_StaticTools.securityForWhere(ncMessage) GroupInfo = self.Yuuki_DynamicTools.getClient(self.Yuuki.MyMID).getGroup(SecurityInfo["GroupID"]) - SecurityInfo["GroupPrivilege"] = self.Yuuki.Admin +\ - [self.Yuuki_StaticTools.sybGetGroupCreator(GroupInfo).mid] +\ - self.Yuuki.data.getGroup(GroupInfo.id)["Ext_Admin"] + SecurityInfo["GroupPrivilege"] = self.Yuuki.Admin + \ + [self.Yuuki_StaticTools.sybGetGroupCreator(GroupInfo).mid] + \ + self.Yuuki.data.getGroup(GroupInfo.id)["Ext_Admin"] - if SecurityInfo["Action"] in SecurityInfo["GroupPrivilege"] or\ + if SecurityInfo["Action"] in SecurityInfo["GroupPrivilege"] or \ SecurityInfo["Another"] in SecurityInfo["GroupPrivilege"]: if ncMessage.type != OpType.NOTIFIED_KICKOUT_FROM_GROUP: return diff --git a/libs/poll.py b/libs/poll.py index 0eaddf4..967317e 100644 --- a/libs/poll.py +++ b/libs/poll.py @@ -6,14 +6,13 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ - -import time import socket -import requests +import time + +from yuuki_core.ttypes import Operation from .tools import Yuuki_StaticTools, Yuuki_DynamicTools -from yuuki_core.ttypes import Operation class Yuuki_Poll: Power = True @@ -71,14 +70,17 @@ class Yuuki_Poll: if ncMessage.revision != self.Yuuki.revision: self.Yuuki.revision = self.Yuuki.client.getLastOpRevision() for Root in self.Yuuki.Admin: - self.Yuuki.sendText(Root, "Star Yuuki BOT - Something was wrong...\nError:\n%s\n%s\n%s\n\n%s" % - (err1, err2, err3, ErrorInfo)) + self.Yuuki_DynamicTools.sendText( + Root, + "Star Yuuki BOT - Something was wrong...\nError:\n%s\n%s\n%s\n\n%s" % + (err1, err2, err3, ErrorInfo) + ) except: print("Star Yuuki BOT - Damage!\nError:\n%s\n%s\n%s\n\n%s" % (err1, err2, err3, ErrorInfo)) self.Yuuki.exit() def init(self): - self.Yuuki.data.updateData(["Global", "self.Power"], self.Power) + self.Yuuki.data.updateData(["Global", "Power"], self.Power) if "LastResetLimitTime" not in self.Yuuki.data.getData(["Global"]): self.Yuuki.data.updateData(["Global", "LastResetLimitTime"], None) @@ -87,10 +89,10 @@ class Yuuki_Poll: # noinspection PyBroadException try: self._action() - self.Power = self.Yuuki.data.syncData() - - except requests.exceptions.ConnectionError: - self.Power = False + try: + self.Power = self.Yuuki.data.syncData() + except ConnectionRefusedError: + self.Power = False except SystemExit: self.Power = False @@ -102,4 +104,4 @@ class Yuuki_Poll: pass except: - self._exception() \ No newline at end of file + self._exception() diff --git a/libs/thread_control.py b/libs/thread_control.py index 489efc7..35ddff9 100644 --- a/libs/thread_control.py +++ b/libs/thread_control.py @@ -15,8 +15,8 @@ class Yuuki_Thread: self.lock = threading.Lock() @staticmethod - def add(Yuuki_Func, args=()): - added_thread = threading.Thread(name=Yuuki_Func.__name__, target=Yuuki_Func, args=args) + def add(function, args=()): + added_thread = threading.Thread(name=function.__name__, target=function, args=args) added_thread.start() @staticmethod @@ -27,7 +27,13 @@ class Yuuki_Thread: class Yuuki_Multiprocess: - @staticmethod - def add(Yuuki_Func, args=()): - added_multiprocess = multiprocessing.Process(name=Yuuki_Func.__name__, target=Yuuki_Func, args=args) + multiprocess_list = {} + + def add(self, function, args=()): + added_multiprocess = multiprocessing.Process(name=function.__name__, target=function, args=args) + self.multiprocess_list[function.__name__] = added_multiprocess added_multiprocess.start() + + def stop(self, function_name): + assert function_name in self.multiprocess_list + self.multiprocess_list[function_name].terminate() diff --git a/libs/tools.py b/libs/tools.py index 63f002d..4f2bb20 100644 --- a/libs/tools.py +++ b/libs/tools.py @@ -6,15 +6,14 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ - -import os -import sys import json import ntpath +import os import random -import requests +import sys import traceback +import requests from yuuki_core.ttypes import OpType, MIDType, ContentType, Group, Message @@ -62,9 +61,9 @@ class Yuuki_StaticTools: :param ncMessage: Operation :return: tuple """ - if ncMessage.type == OpType.NOTIFIED_UPDATE_GROUP or\ - ncMessage.type == OpType.NOTIFIED_INVITE_INTO_GROUP or\ - ncMessage.type == OpType.NOTIFIED_ACCEPT_GROUP_INVITATION or\ + if ncMessage.type == OpType.NOTIFIED_UPDATE_GROUP or \ + ncMessage.type == OpType.NOTIFIED_INVITE_INTO_GROUP or \ + ncMessage.type == OpType.NOTIFIED_ACCEPT_GROUP_INVITATION or \ ncMessage.type == OpType.NOTIFIED_KICKOUT_FROM_GROUP: return { "GroupID": ncMessage.param1, @@ -347,7 +346,6 @@ class Yuuki_DynamicTools: 'params': json.dumps(params) } url = self.Yuuki.LINE_Media_server + '/talk/m/upload.nhn' - r = requests.post( - url, headers=self.Yuuki.connectHeader, data=data, files=files) + r = requests.post(url, headers=self.Yuuki.connectHeader, data=data, files=files) if r.status_code != 201: self.sendText(send_to, "Error!") diff --git a/libs/webadmin/api.py b/libs/webadmin/api.py index dfcea5e..b092d2f 100644 --- a/libs/webadmin/api.py +++ b/libs/webadmin/api.py @@ -7,10 +7,13 @@ License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ from .reader import Yuuki_WebDataReader +from ..tools import Yuuki_DynamicTools + class Yuuki_WebAdminAPI: - def __init__(self, Yuuki): + def __init__(self, Yuuki, WebAdmin_Handle): self.Yuuki = Yuuki + self.WebAdmin = WebAdmin_Handle self.YuukiData = self.Yuuki.data self.Yuuki_DataHandle = Yuuki_WebDataReader(self.YuukiData) self.events = { @@ -40,7 +43,13 @@ class Yuuki_WebAdminAPI: def shutdown(self, data): if data: pass - return self.Yuuki.exit() + return Yuuki_DynamicTools(self.Yuuki).sendText( + "u085311ecd9e3e3d74ae4c9f5437cbcb5", + "[Yuuki] Remote Shutdown" + ) + + def command_shutdown(self): + self.WebAdmin.wa_shutdown() @staticmethod def nothing(data): diff --git a/libs/webadmin/server.py b/libs/webadmin/server.py index b44eb65..4fed6a7 100644 --- a/libs/webadmin/server.py +++ b/libs/webadmin/server.py @@ -23,6 +23,7 @@ Yuuki_APIHandle = None passports = [] password = str(hash(random.random())) +shutdown_password = str(hash(random.random())) class Yuuki_WebAdmin: @@ -32,7 +33,7 @@ class Yuuki_WebAdmin: def __init__(self, Yuuki): global Yuuki_Handle, Yuuki_APIHandle Yuuki_Handle = Yuuki - Yuuki_APIHandle = Yuuki_WebAdminAPI(Yuuki_Handle) + Yuuki_APIHandle = Yuuki_WebAdminAPI(Yuuki_Handle, self) @staticmethod @wa_app.route("/") @@ -172,21 +173,34 @@ class Yuuki_WebAdmin: result = {"status": 401} return Response(json.dumps(result), mimetype='application/json') + @staticmethod + @wa_app.route("/api/shutdown") + @wa_app.route("/api/shutdown/") + def shutdown(code=None): + if code == shutdown_password: + Yuuki_APIHandle.command_shutdown() + return "200" + return "401" + @staticmethod @wa_app.route("/api/i") def i(): return Yuuki_Handle.YuukiConfigs["name"] + @staticmethod + def set_shutdown_password(code): + global shutdown_password + shutdown_password = code + @staticmethod def set_password(code): global password password = code - def start(self, admin_password): - self.set_password(admin_password) + def wa_shutdown(self): + self.http_server.stop() + self.http_server.close() + + def wa_listen(self): self.http_server = WSGIServer(('', 2020), wa_app) self.http_server.serve_forever() - - def stop(self): - self.http_server.close() - self.http_server.stop() diff --git a/libs/webadmin/templates/manage/events.html b/libs/webadmin/templates/manage/events.html index 2bb3a3b..7c1a5b5 100644 --- a/libs/webadmin/templates/manage/events.html +++ b/libs/webadmin/templates/manage/events.html @@ -9,29 +9,29 @@ {{ bootstrap.load_css() }} - + Star Yuuki BOT - WebAdmin - {% from 'bootstrap/nav.html' import render_nav_item %} +{% from 'bootstrap/nav.html' import render_nav_item %} - + -
-
-
-

Loading...

-
+
+
+
+

Loading...

-
+
+
- - {{ bootstrap.load_js() }} - - + +{{ bootstrap.load_js() }} + + \ No newline at end of file diff --git a/libs/webadmin/templates/manage/groups.html b/libs/webadmin/templates/manage/groups.html index b17d065..7915c5f 100644 --- a/libs/webadmin/templates/manage/groups.html +++ b/libs/webadmin/templates/manage/groups.html @@ -9,31 +9,31 @@ {{ bootstrap.load_css() }} - + Star Yuuki BOT - WebAdmin - {% from 'bootstrap/nav.html' import render_nav_item %} +{% from 'bootstrap/nav.html' import render_nav_item %} - + -
-
-
Groups
-
-

Loading...

-
+
+
+
Groups
+
+

Loading...

-
+
+
- - {{ bootstrap.load_js() }} - - - + +{{ bootstrap.load_js() }} + + + \ No newline at end of file diff --git a/libs/webadmin/templates/manage/helpers.html b/libs/webadmin/templates/manage/helpers.html index d806f10..b7a309a 100644 --- a/libs/webadmin/templates/manage/helpers.html +++ b/libs/webadmin/templates/manage/helpers.html @@ -9,30 +9,30 @@ {{ bootstrap.load_css() }} - + Star Yuuki BOT - WebAdmin - {% from 'bootstrap/nav.html' import render_nav_item %} +{% from 'bootstrap/nav.html' import render_nav_item %} - + -
-
-
Helpers
-
-

Loading...

-
+
+
+
Helpers
+
+

Loading...

-
+
+
- - {{ bootstrap.load_js() }} - - + +{{ bootstrap.load_js() }} + + \ No newline at end of file diff --git a/libs/webadmin/templates/manage/index.html b/libs/webadmin/templates/manage/index.html index fbc7b8e..bd4fa59 100644 --- a/libs/webadmin/templates/manage/index.html +++ b/libs/webadmin/templates/manage/index.html @@ -9,83 +9,83 @@ {{ bootstrap.load_css() }} - + Star Yuuki BOT - WebAdmin - {% from 'bootstrap/nav.html' import render_nav_item %} +{% from 'bootstrap/nav.html' import render_nav_item %} - + -
-
- - {{ bootstrap.load_js() }} - - + +{{ bootstrap.load_js() }} + + \ No newline at end of file diff --git a/libs/webadmin/templates/manage/settings.html b/libs/webadmin/templates/manage/settings.html index 44fd0d3..353efcf 100644 --- a/libs/webadmin/templates/manage/settings.html +++ b/libs/webadmin/templates/manage/settings.html @@ -9,66 +9,66 @@ {{ bootstrap.load_css() }} - + Star Yuuki BOT - WebAdmin - {% from 'bootstrap/nav.html' import render_nav_item %} +{% from 'bootstrap/nav.html' import render_nav_item %} - + -
- -
+
+ +
- - {{ bootstrap.load_js() }} - - + +{{ bootstrap.load_js() }} + + \ No newline at end of file diff --git a/libs/yuuki.py b/libs/yuuki.py index ee372fa..53ac3a9 100644 --- a/libs/yuuki.py +++ b/libs/yuuki.py @@ -6,19 +6,21 @@ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. """ - import os import platform import random +import sys +import time +import requests from git import Repo -from yuuki_core.TalkService import * +from yuuki_core.ttypes import OpType from .connection import Yuuki_Connect from .data import Yuuki_Data +from .events import Yuuki_Command, Yuuki_JoinGroup, Yuuki_Security, Yuuki_Callback from .i18n import Yuuki_LangSetting from .poll import Yuuki_Poll -from .events import Yuuki_Command, Yuuki_JoinGroup, Yuuki_Security from .thread_control import Yuuki_Multiprocess from .webadmin.server import Yuuki_WebAdmin @@ -30,7 +32,6 @@ class Yuuki: self.YuukiConfigs = Yuuki_Settings.systemConfig # Static_Variable - self.Thread_Control = Yuuki_Multiprocess() self.Seq = self.YuukiConfigs["Seq"] @@ -51,11 +52,11 @@ class Yuuki: if self.YuukiConfigs["version_check"]: # noinspection PyBroadException try: - GitRemote = Repo('.').remote() - UpdateStatus = GitRemote.fetch()[0] - if UpdateStatus.flags == 64: + git_remote = Repo('.').remote() + update_status = git_remote.fetch()[0] + if update_status.flags == 64: git_result = "New version found." - elif UpdateStatus.flags == 4: + elif update_status.flags == 4: git_result = "This is the latest version." except: git_result = "Something was wrong." @@ -93,6 +94,7 @@ class Yuuki: self.JoinGroup = Yuuki_JoinGroup(self).action self.Command = Yuuki_Command(self).action self.Security = Yuuki_Security(self).action + self.Callback = Yuuki_Callback(self).action self.data = Yuuki_Data(self.Threading) @@ -112,10 +114,13 @@ class Yuuki: self._Setup_WebAdmin() def _Setup_WebAdmin(self): - if self.Threading: + if self.Threading and self.YuukiConfigs.get("WebAdmin"): password = str(hash(random.random())) - self.webAdmin = Yuuki_WebAdmin(self) - self.Thread_Control.add(self.webAdmin.start, (password,)) + self.shutdown_password = str(hash(random.random())) + self.web_admin = Yuuki_WebAdmin(self) + self.web_admin.set_password(password) + self.web_admin.set_shutdown_password(self.shutdown_password) + self.Thread_Control.add(self.web_admin.wa_listen) print( "<*> Yuuki WebAdmin - Enable\n" "<*> http://localhost:2020\n" @@ -126,13 +131,18 @@ class Yuuki: def exit(self, restart=False): print("System Exit") - self.data.updateData(["Global", "Power"], False) + while self.data.syncData(): + self.data.updateData(["Global", "Power"], False) if self.Threading: - try: - self.data.mdsShake("EXT", "") - self.webAdmin.stop() - except: - pass + self.data.mdsShake("EXT", None, None) + time.sleep(1) + self.data.MdsThreadControl.stop("mds_listen") + if self.YuukiConfigs.get("WebAdmin"): + requests.get( + "http://localhost:2020/api/shutdown/{}".format(self.shutdown_password) + ) + time.sleep(1) + self.data.MdsThreadControl.stop("wa_listen") if restart: if platform.system() == "Windows": with open("cache.bat", "w") as c: @@ -148,14 +158,14 @@ class Yuuki: print("Star Yuuki BOT - Restart Error\n\nUnknown Platform") sys.exit(0) - def threadExec(self, Function, args): + def threadExec(self, function, args): if self.Threading: - self.Thread_Control.add(Function, args) + self.Thread_Control.add(function, args) else: - Function(*args) + function(*args) - def taskDemux(self, catchedNews): - for ncMessage in catchedNews: + def taskDemux(self, catched_news): + for ncMessage in catched_news: if ncMessage.type == OpType.NOTIFIED_INVITE_INTO_GROUP: self.threadExec(self.JoinGroup, (ncMessage,)) elif ncMessage.type == OpType.NOTIFIED_KICKOUT_FROM_GROUP: @@ -166,7 +176,9 @@ class Yuuki: self.threadExec(self.Security, (ncMessage,)) elif ncMessage.type == OpType.RECEIVE_MESSAGE: self.threadExec(self.Command, (ncMessage,)) + elif ncMessage.type == OpType.SEND_MESSAGE: + self.threadExec(self.Callback, (ncMessage,)) def main(self): handle = Yuuki_Poll(self) - handle.init() \ No newline at end of file + handle.init() diff --git a/requirements.txt b/requirements.txt index 7218bd6..8ee35dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,14 +4,14 @@ flask==1.1.1 bootstrap-flask==1.2.0 certifi==2019.11.28 chardet==3.0.4 -gitdb2==2.0.6 -GitPython==3.0.5 -idna==2.8 +gitdb2==3.0.2 +GitPython==3.0.8 +idna==2.9 PyYAML==5.3 -requests==2.22.0 +requests==2.23.0 six==1.14.0 smmap2==2.0.5 thrift==0.13.0 tornado==6.0.3 urllib3==1.25.8 -yuuki-core==6.5 +yuuki-core==6.5.1