Added instant messaging api
This commit is contained in:
parent
cfa9ab049e
commit
5b7aafa61c
|
@ -1,3 +1,4 @@
|
|||
.idea/
|
||||
Packages/
|
||||
Chrome/
|
||||
PyQt5/
|
||||
|
|
134
Galactic.py
134
Galactic.py
|
@ -14,6 +14,7 @@ import inspect
|
|||
import uvicorn
|
||||
import zipfile
|
||||
import hashlib
|
||||
import asyncio
|
||||
import tempfile
|
||||
import platform
|
||||
import requests
|
||||
|
@ -42,9 +43,10 @@ from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QSystemTrayI
|
|||
from PyQt5.QtCore import Qt, QCoreApplication, QTimer, QUrl
|
||||
from PyQt5.QtGui import QIcon
|
||||
from typing import List
|
||||
from fastapi import FastAPI, Response, UploadFile, File, HTTPException
|
||||
from fastapi import FastAPI, Response, WebSocket, UploadFile, File, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse, FileResponse
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
from winotify import Notification, audio
|
||||
from func_timeout import func_set_timeout, FunctionTimedOut
|
||||
from pathlib import Path
|
||||
|
@ -1069,31 +1071,66 @@ class Browser(InspectRequestsMixin, DriverCommonMixin, Chrome):
|
|||
len(self.window_handles) > 0 and self.switch_to.window(self.window_handles[0])
|
||||
|
||||
|
||||
class BrowserPluginRunningParam(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
pass
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
pass
|
||||
|
||||
def __getitem__(self, item):
|
||||
try:
|
||||
return super().__getitem__(item)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __getattr__(self, item):
|
||||
try:
|
||||
return super().__getitem__(item)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class BrowserPluginParent:
|
||||
id = None
|
||||
name = None
|
||||
requirements = []
|
||||
|
||||
def __init__(self):
|
||||
self.thread = None
|
||||
self.thread_exception = None
|
||||
self.app_id = None
|
||||
self.thread = None
|
||||
self.run_task_exception = None
|
||||
self.user_id = None
|
||||
self.instant_message = None
|
||||
|
||||
def _run_task(self, *args, **kwargs):
|
||||
try:
|
||||
self.running(*args, **kwargs)
|
||||
except Exception as e:
|
||||
self.thread_exception = e
|
||||
self.notification_except(e)
|
||||
self.run_task_exception = e
|
||||
self.message_except(e)
|
||||
|
||||
def notification(self, message=None):
|
||||
def message(self, message=None):
|
||||
notification_send(app_id=self.app_id, title=self.name, message='%s' % (message,))
|
||||
self.instant_message and self.instant_message(json.dumps({
|
||||
'time': int(time.time()), 'type': 'message', 'data': {'user_id': str(self.user_id), 'content': str(message), 'title': str(self.app_id)}
|
||||
}))
|
||||
|
||||
def notification_except(self, exception_info):
|
||||
def message_except(self, exception_info):
|
||||
notification_send(app_id=self.app_id, title='%s ' % (self.name,), message='%s' % (exception_info,))
|
||||
|
||||
def run(self, *args, **kwargs) -> threading.Thread:
|
||||
thread = threading.Thread(target=self._run_task, args=args, kwargs={**kwargs, **{'notification': self.notification}})
|
||||
def logging(self, logging=None):
|
||||
self.instant_message and self.instant_message(json.dumps({
|
||||
'time': int(time.time()), 'type': 'logging', 'data': {'user_id': str(self.user_id), 'content': str(logging)}
|
||||
}))
|
||||
|
||||
def run(self, app_id, user_id, driver, requirements, instant_message) -> threading.Thread:
|
||||
self.app_id, self.user_id, self.instant_message = app_id, user_id, instant_message
|
||||
params = BrowserPluginRunningParam({'driver': driver, 'message': self.message, 'logging': self.logging, 'requirements': requirements})
|
||||
thread = threading.Thread(target=self._run_task, args=(params,))
|
||||
self.thread = thread
|
||||
thread.daemon = False
|
||||
thread.start()
|
||||
|
@ -1111,8 +1148,8 @@ class BrowserPluginParent:
|
|||
return self.thread.is_alive() if self.thread else None
|
||||
|
||||
@staticmethod
|
||||
def running(driver: Browser, notification, requirements):
|
||||
time.sleep(5)
|
||||
def running(param: BrowserPluginRunningParam):
|
||||
param.driver.wait(5)
|
||||
|
||||
|
||||
class BrowserPluginFileTest(BrowserPluginParent):
|
||||
|
@ -1125,8 +1162,8 @@ class BrowserPluginFileTest(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
notification(requirements)
|
||||
def running(param):
|
||||
param.message(param.requirements)
|
||||
|
||||
|
||||
class BrowserPluginMultFileTest(BrowserPluginParent):
|
||||
|
@ -1139,8 +1176,8 @@ class BrowserPluginMultFileTest(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
notification(requirements)
|
||||
def running(param):
|
||||
param.message(param.requirements)
|
||||
|
||||
|
||||
class BrowserPluginTextTest(BrowserPluginParent):
|
||||
|
@ -1153,8 +1190,8 @@ class BrowserPluginTextTest(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
notification(requirements)
|
||||
def running(param):
|
||||
param.message(param.requirements)
|
||||
|
||||
|
||||
class BrowserPluginMultTextTest(BrowserPluginParent):
|
||||
|
@ -1167,8 +1204,8 @@ class BrowserPluginMultTextTest(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
notification(requirements)
|
||||
def running(param):
|
||||
param.message(param.requirements)
|
||||
|
||||
|
||||
class BrowserPluginFileCommandDebug(BrowserPluginParent):
|
||||
|
@ -1181,8 +1218,8 @@ class BrowserPluginFileCommandDebug(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
file = requirements[0][0]['path']
|
||||
def running(param):
|
||||
file = param.requirements[0][0]['path']
|
||||
exec(open(file, mode='r', encoding='utf-8').read())
|
||||
|
||||
|
||||
|
@ -1196,8 +1233,8 @@ class BrowserPluginTextCommandDebug(BrowserPluginParent):
|
|||
]
|
||||
|
||||
@staticmethod
|
||||
def running(driver, notification, requirements):
|
||||
code = requirements[0]
|
||||
def running(param):
|
||||
code = param.requirements[0]
|
||||
code and exec(code)
|
||||
|
||||
|
||||
|
@ -1368,7 +1405,8 @@ class BrowserManagerUserRunning:
|
|||
|
||||
|
||||
class BrowserManager:
|
||||
def __init__(self, driver: str, binary: str, manager_data_file: str, browser_data_home: str, browser_init_home: str, use_selenium_wire: int = 0):
|
||||
def __init__(self, runner, driver: str, binary: str, manager_data_file: str, browser_data_home: str, browser_init_home: str, use_selenium_wire: int = 0):
|
||||
self.runner = runner
|
||||
if not os.path.exists(driver):
|
||||
raise FileNotFoundError('The driver executable file does not exist.')
|
||||
if not os.path.exists(binary):
|
||||
|
@ -1429,7 +1467,7 @@ class BrowserManager:
|
|||
|
||||
def _get_user_app_id(self, user_id: str):
|
||||
user_name = self.data_storage['browser_user'][user_id]['user_name']
|
||||
return '%s%s%s' % (user_id, user_name and ' - ', user_name or '')
|
||||
return user_name or user_id
|
||||
|
||||
def _initialize_user_running(self, user_id: str):
|
||||
return BrowserManagerUserRunning(
|
||||
|
@ -1576,8 +1614,7 @@ class BrowserManager:
|
|||
if (driver is None) == 1:
|
||||
raise Exception('No driver object.')
|
||||
plugin = self.plugins[plugin_id]()
|
||||
plugin.app_id = self._get_user_app_id(user_id)
|
||||
plugin.run(driver=driver, requirements=requirements)
|
||||
plugin.run(self._get_user_app_id(user_id), user_id, driver, requirements, self.runner.web_server.websocket_connection_manager.send_broadcast_use_sync)
|
||||
running.set_plugin(plugin)
|
||||
finally:
|
||||
self.user_operate_complete(user_id)
|
||||
|
@ -1903,12 +1940,40 @@ class WebServerAPIJSONData(BaseModel):
|
|||
data: dict = None
|
||||
|
||||
|
||||
class WebSocketConnectionManager:
|
||||
def __init__(self):
|
||||
self.active_connections = []
|
||||
|
||||
async def connect(self, websocket: WebSocket, disconnect=None):
|
||||
if disconnect:
|
||||
self.active_connections.remove(websocket)
|
||||
else:
|
||||
await websocket.accept()
|
||||
await websocket.send_text('Hello, %s:%s' % (websocket.client.host, websocket.client.port))
|
||||
self.active_connections.append(websocket)
|
||||
|
||||
async def send_broadcast(self, message: str):
|
||||
dead_connections = []
|
||||
for conn in self.active_connections:
|
||||
try:
|
||||
await conn.send_text(str(message))
|
||||
except WebSocketDisconnect:
|
||||
dead_connections.append(conn)
|
||||
for conn in dead_connections:
|
||||
await self.connect(conn, 1)
|
||||
|
||||
def send_broadcast_use_sync(self, text):
|
||||
asyncio.run(self.send_broadcast(text))
|
||||
|
||||
|
||||
class WebServer:
|
||||
def __init__(self, root: str, data: str, default_plugins=None):
|
||||
def __init__(self, runner, root: str, data: str, default_plugins=None):
|
||||
self.runner = runner
|
||||
self.app = FastAPI()
|
||||
self.www = os.path.join(root, 'www')
|
||||
self.upload_dir = os.path.join(data, 'upload')
|
||||
self.browser_manager = BrowserManager(
|
||||
runner=runner,
|
||||
driver=os.path.join(root, 'Chrome/chromedriver.exe'),
|
||||
binary=os.path.join(root, 'Chrome/chrome.exe'),
|
||||
manager_data_file=os.path.join(data, 'manager.json'),
|
||||
|
@ -1918,6 +1983,17 @@ class WebServer:
|
|||
)
|
||||
self.browser_manager.load_plugins(default_plugins, is_external=0)
|
||||
self.browser_manager.load_plugins_from_external_module()
|
||||
self.websocket_connection_manager = WebSocketConnectionManager()
|
||||
|
||||
@self.app.websocket('/instant_message')
|
||||
async def websocket_instant_message(websocket: WebSocket):
|
||||
await self.websocket_connection_manager.connect(websocket)
|
||||
try:
|
||||
while True:
|
||||
await websocket.receive_text()
|
||||
except WebSocketDisconnect:
|
||||
pass
|
||||
await self.websocket_connection_manager.connect(websocket, 1)
|
||||
|
||||
@self.app.api_route(methods=['POST'], path='/plugin_run')
|
||||
def api_plugin_run(data: WebServerAPIJSONData):
|
||||
|
@ -2099,7 +2175,7 @@ class MainRunner:
|
|||
|
||||
def run(self):
|
||||
os.path.exists(self.app_data) or os.makedirs(self.app_data)
|
||||
self.web_server = WebServer(root=self.app_root, data=self.app_data, default_plugins=self.plugin_list)
|
||||
self.web_server = WebServer(runner=self, root=self.app_root, data=self.app_data, default_plugins=self.plugin_list)
|
||||
self.web_server_thread = threading.Thread(target=self.web_server.run, kwargs={'host': self.web_server_host, 'port': self.web_server_port})
|
||||
self.web_server_thread.daemon = True
|
||||
self.web_server_thread.start()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue