def execute_selenium_script(request: ServiceRequestMessage): """执行Selenium脚本 支持脚本文件或者具体的Python组件,会自动将工作单元参数和web driver对象注入到脚本当中 - name: selenium_script type: browser.selenium args: request: session_id: var("session_id") script: str("file:///Users/chihongze/test.py") arg1: xxxx arg2: xxxx OR - name: selenium_script type: browser.selenium args: request: session_id: var("session_id") component: str("my.rpa.test") arg1: xxxx arg2: xxx """ args = request.args session_id = args["session_id"] from playwell_rpa.browser import web_driver_manager web_driver = web_driver_manager.get_driver(session_id) if "script" in args: with open(args["script"], "r") as f: code = f.read() exec( code, { "web_driver": web_driver, "activity_id": request.activity_id, "domain_id": request.domain_id, "args": args }) return Result.ok() elif "component" in args: component = pydoc.locate(args["component"]) if not callable(component): return Result.fail(error_code="invalid_component", message="The component is not callable") result = component(web_driver, request.activity_id, request.domain_id, args) if result is None: return Result.ok() else: return Result.ok({"result": result}) else: return Result.fail(error_code="invalid_arg", message="没有指定要执行的Selenium脚本或组件")
def _post_handler(): try: message_data_seq = web_request.json for message_data in message_data_seq: self._buffer.put_nowait(self._decode_message(message_data)) return Result.ok().to_dict() except Exception as e: logging.exception(e) return Result.fail( error_code="service_error", message=str(e) ).to_dict()
def _batch_test(): headers = { 'Authorization': 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"', 'X-WSSE': _build_wsse_header("m97WWjK5FtIV7h29jsnWCWDb4I8e", "F13400QeCtVjUQ5Vm74Sj8zIw805") } sms_contents = [{ "to": ["17600817832"], "templateId": "8856354b9b7e495fb6bd933882b9f9db", "templateParas": ["Chihz"], "signature": "企智未来" }] api_base = "https://api.rtc.huaweicloud.com:10443" channel_id = "8819080633619" body = {"from": channel_id, "smsContent": sms_contents} try: response = requests.post(url=urljoin(api_base, "/sms/batchSendDiffSms/v1"), headers=headers, json=body, verify=False) print(headers) print(response.text) send_result = response.json() if send_result["code"] == "000000": all_sms_status = { sms_status["originTo"]: sms_status["status"] for sms_status in send_result["result"] } results = [] for sms_content in sms_contents: status = all_sms_status[sms_content["to"][0]] if status == "000000": results.append(Result.ok()) else: results.append(Result.fail(error_code=status)) return results else: return [Result.fail(error_code=send_result["code"]) ] * len(sms_contents) except Exception as e: logging.exception(e) return [Result.fail(error_code="sys_error", message=str(e)) ] * len(sms_contents)
def table_to_messages(request: ServiceRequestMessage): """可将表格转化为消息的服务 - name: table.to_messages args: request: table: ref("memory_table", "students") message_bus: str("target_message_bus") message_type: str("notify_student") columns: - str("ID") - str("Name") - str("Phone") batch_size: 1000 sleep: 5 """ args = request.args table = locate_from_request("table", request) message_type, message_bus_name = args["message_type"], args["message_bus"] col_names = args.get("columns", []) batch_size, sleep = args.get("batch_size", 1000), args.get("sleep", 0) message_bus = message_bus_manager.get_message_bus(message_bus_name) if message_bus is None: return Result.fail(error_code="bus_not_found", message="Could not found message bus: %s" % message_bus_name) messages = [] ts = int(time.time() * 1000) for idx, row in enumerate(table): messages.append( Message(type=message_type, sender="", receiver="", attr=row.view(col_names).map, time=ts)) if idx % batch_size == 0: message_bus.write(messages) messages = [] ts = int(time.time() * 1000) if sleep != 0: time.sleep(sleep) if messages: message_bus.write(messages) return Result.ok()
def __init__(self, sender: str, receiver: str, time: int, activity_id: int, domain_id: str, action: str, status: str, error_code: str = "", message: str = "", data: dict = None): super().__init__(type=MessageTypes.RESPONSE, sender=sender, receiver=receiver, attr={ ServiceResponseMessage.ATTR_ACTIVITY: activity_id, ServiceResponseMessage.ATTR_DOMAIN: domain_id, ServiceResponseMessage.ATTR_ACTION: action, ServiceResponseMessage.ATTR_STATUS: status, ServiceResponseMessage.ATTR_ERROR_CODE: error_code, ServiceResponseMessage.ATTR_MESSAGE: message, ServiceResponseMessage.ATTR_DATA: data }, time=time) self._activity_id = activity_id self._domain_id = domain_id self._action = action self._result = Result( status=status, error_code=error_code if error_code is not None else "", message=message if message is not None else "", data=data if data is not None else {})
def declare_new_table(request: ServiceRequestMessage): """声明表格的元信息 - name: create_table type: table.declare args: request: table: ref("excel_table", "students") columns: - name: str("id") title: str("ID") - name: str("name") title: str("姓名") meta: path: str("/tmp/test.xlsx") sheet: str("sheet") has_title: yes """ args = request.args type, name, _, tmp = args["table"] columns = [Column(**col_data) for col_data in args.get("columns", [])] meta = args.get("meta", {}) declare_table(type=type, activity_id=request.activity_id, domain_id=request.domain_id, name=name, columns=columns, meta=meta, tmp=tmp) return Result.ok()
def to_table(request: ServiceRequestMessage): """将HTML中的指定元素和属性提取成为Table对象 - name: html_to_table type: html.to_table args: request: html: ref("memory_text", "html_text") elements: str(".gl-i-wrap") table: ref("excel_type", "the_table") columns: - element: str(".p-name a") attr: str("title") - element: str(".p-price .i") type: str("decimal") default: str("xxx") """ args = request.args html_content = _get_html_content("html", request) bs = BeautifulSoup(html_content, 'html.parser') elements = bs.select(args["elements"]) rows = [] for element in elements: row = [] for declare in args["columns"]: row.append(_get_value_from_element(element, declare)) rows.append(row) table = locate_from_request("table", request) table.append_rows(rows) return Result.ok()
def __call__(self, requests: Sequence[ServiceRequestMessage]): connection = pymysql.connect(cursorclass=pymysql.cursors.DictCursor, **self._config) try: with connection.cursor() as cursor: for req in requests: sql = req.args["sql"] params = req.args["params"] cursor.execute(sql, params) connection.commit() return [Result.ok()] * len(requests) except Exception as e: logging.exception(e) return [Result.fail(error_code="sys_error", message=str(e)) ] * len(requests) finally: connection.close()
def _batch_send(self, sms_contents: Sequence[dict]): try: headers = { 'Authorization': 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"', 'X-WSSE': _build_wsse_header(self._app_key, self._app_secret) } print(sms_contents) body = {"from": self._channel_id, "smsContent": sms_contents} response = requests.post(url=urljoin(self._api_base, "/sms/batchSendDiffSms/v1"), headers=headers, json=body, verify=False) send_result = response.json() if send_result["code"] == "000000": all_sms_status = { sms_status["originTo"]: sms_status["status"] for sms_status in send_result["result"] } results = [] for sms_content in sms_contents: status = all_sms_status[sms_content["to"][0]] if status == "000000": results.append(Result.ok()) else: results.append(Result.fail(error_code=status)) return results else: return [Result.fail(error_code=send_result["code"]) ] * len(sms_contents) except Exception as e: logging.exception(e) return [Result.fail(error_code="sys_error", message=str(e)) ] * len(sms_contents)
def register_service_meta(self, service_meta: ServiceMeta): """Register service meta """ from playwell.service.config import playwell_api response = requests.post(urljoin(playwell_api, "/v1/service_meta/register"), json={ "name": service_meta.name, "message_bus": service_meta.message_bus, "config": service_meta.config }) return Result.from_response(response)
def close_browser(request: ServiceRequestMessage): """关闭一个浏览器会话 - name: close_browser type: browser.close args: request: session_id: var("session_id") """ session_id = request.args["session_id"] from playwell_rpa.browser import web_driver_manager web_driver_manager.close_driver(session_id) return Result.ok()
def refresh_all(self): """Refresh all service meta from API """ from playwell.service.config import playwell_api result = Result.from_response( requests.get(urljoin(playwell_api, "/v1/service_meta/all"))) if not result.is_ok(): raise PlaywellServiceException( "Refresh service meta failure, the API return: %s" % result) self._all_service_meta = { service_meta["name"]: ServiceMeta(**service_meta) for service_meta in result.data["services"] }
def register_input_message_bus(self): """注册input message bus """ from playwell.service.config import input_message_bus_config if not input_message_bus_config: return if self._input_message_bus is not None: raise PlaywellServiceException( "The input message bus has already been registered!") from playwell.service.config import playwell_api response = requests.post(urljoin(playwell_api, "/v1/message_bus/register"), json={"config": input_message_bus_config}) result = Result.from_response(response) if not result.is_ok(): raise PlaywellServiceException( "Register message bus error, the API return %s" % result) response = requests.post( urljoin(playwell_api, "/v1/message_bus/open"), json={"name": input_message_bus_config["name"]}) result = Result.from_response(response) if not result.is_ok() and result.error_code != "already_opened": raise PlaywellServiceException( "Open message bus error, the API return: %s" % result) from playwell.service.message.http import HttpMessageBus self._input_message_bus = HttpMessageBus( name=input_message_bus_config["name"], clazz=input_message_bus_config["class"], alive=True, opened=True, available=True, config=input_message_bus_config) self._input_message_bus.init_web_server() self._all_playwell_message_bus[ self._input_message_bus.name] = self._input_message_bus
def table_to_list(request: ServiceRequestMessage): """将表格转化成list,并在Result中返回 - name: table.to_list args: request: table: ref("excel_table", "students") row_style: str("list") columns: - str("id") - str("name") timeout: minutes(1) ctrl: - when: resultOk() context_vars: list: resultVar("list") then: call("next_action") - default: failBecause("to_list_failure") """ table = locate_from_request("table", request) row_style = request.args.get("row_style", "map") col_names = request.args.get("columns", []) if table: lst = [] for row in table: row = row.view(col_names) if row_style == "list": lst.append(row.elements) elif row_style == "map": lst.append(row.map) else: return Result.fail(error_code="invalid_row_style", message=("Invalid row style: %s" % row_style)) else: lst = [] return Result.ok(data={"list": lst})
def submit_form(request: ServiceRequestMessage): """该Action可以用于自动填充表单并提交 - name: submit_form type: browser.form args: request: session_id: var("session_id") input: - css_selector: str(".username") input: str("Sam") - css_selector: str(".password") input: str("12345") submit: css_selector: str(".submit") """ from playwell_rpa.browser import (web_driver_manager, select_element) args = request.args driver = web_driver_manager.get_driver(args["session_id"]) # handle input elements input_elements = args["input"] for input_element_arg in input_elements: selector, selector_expr, input_value = None, None, None for k, v in input_element_arg.items(): if k.endswith("_selector"): selector = k selector_expr = v elif k == "input": input_value = v input_element = select_element(driver, selector, selector_expr) input_element.click() input_element.send_keys(input_value) # click submit button submit_element_args = args["submit"] selector, selector_expr = submit_element_args.popitem() try: select_element(driver, selector, selector_expr).submit() except Exception as e: logging.warning("Submit error: %s, try click" % str(e)) select_element(driver, selector, selector_expr).click() return Result.ok()
def refresh_all(self): from playwell.service.config import playwell_api response = requests.get(urljoin(playwell_api, "/v1/message_bus/all")) result = Result.from_response(response) if not result.is_ok(): raise PlaywellServiceException( "Refresh message bus failure, the API return %s" % result) message_bus_data_seq = result.data.get("buses", []) all_names = set() for message_bus_data in message_bus_data_seq: name = message_bus_data["name"] clazz = message_bus_data["class"] all_names.add(name) if name in self._all_playwell_message_bus: message_bus = self._all_playwell_message_bus[name] if message_bus.opened != message_bus_data["opened"]: message_bus.opened = message_bus_data["opened"] if message_bus.alive != message_bus_data["alive"]: message_bus.alive = message_bus_data["alive"] if message_bus.available != message_bus_data["available"]: message_bus.available = message_bus_data["available"] else: from playwell.service.message.http import HttpMessageBus if clazz == HttpMessageBus.CLASS_NAME: message_bus = HttpMessageBus( name=name, clazz=clazz, alive=message_bus_data["alive"], opened=message_bus_data["opened"], available=message_bus_data["available"], config=message_bus_data.get("config", {})) self._all_playwell_message_bus[ message_bus.name] = message_bus for message_bus in list(self._all_playwell_message_bus.values()): if message_bus.name not in all_names: del self._all_playwell_message_bus[message_bus.name]
def click(request: ServiceRequestMessage): """click单元可以用于连续点击指定的一系列页面元素 - name: click type: browser.click args: request: session_id: var("session_id") elements: - css_selector: str(".button_a") - name_selector: str("btn") - class_selector: str("btn") """ args = request.args session_id, elements = args["session_id"], args["elements"] from playwell_rpa.browser import (web_driver_manager, select_element) driver = web_driver_manager.get_driver(session_id) for element_info in elements: selector, selector_expr = element_info.popitem() element = select_element(driver, selector, selector_expr) element.click() return Result.ok()
def open_web_page(request: ServiceRequestMessage): """打开一个指定的web页面 如果没有指定session_id,则会打开一个新的浏览器会话 如果指定了session_id,则会直接使用已经存在的浏览器会话 - name: open_browser type: browser.open args: request: browser: str("Chrome") url: str("https://www.apple.com") auto_close: yes wait: timeout: minutes(1) elements: - css_selector: str(".class_name") save_source: ref("source_code", text", "file:///tmp/index.html") gather: - name: str("id") css_selector: str("#id") ctrl: - when: resultOk() then: call("next") context_vars: browser_session_id: resultVar("session_id") """ args = request.args url = args["url"] wait = args.get("wait", {}) gather_elements = args.get("gather", []) from playwell_rpa.browser import (web_driver_manager, select_element) if "browser" in args: session_id = web_driver_manager.create_driver( activity_id=request.activity_id, domain_id=request.domain_id, browser=args["browser"], tmp=args.get("auto_close", True)) else: session_id = args["session_id"] driver = web_driver_manager.get_driver(session_id) if "page_load_timeout" in args: driver.set_page_load_timeout(args["page_load_timeout"]) if "script_timeout" in args: driver.set_script_timeout(args["script_timeout"]) if wait: timeout = wait["timeout"] driver.implicitly_wait(timeout) try: driver.get(url) except TimeoutException: logging.warning("Page load timeout: %s" % url) if wait: elements = wait["elements"] for element in elements: selector, selector_expr = element.popitem() try: select_element(driver, selector, selector_expr) except NoSuchElementException as e: logging.exception(e) return Result.fail(error_code="wait_element_error", message=str(e)) gathered = {} if gather_elements: for ge in gather_elements: try: item_name, text = None, None for k, v in ge.items(): if k == "name": item_name = v if k.endswith("_selector"): text = select_element(driver, k, v).text gathered[item_name] = text except NoSuchElementException as e: logging.exception(e) if "save_source" in args: save_source = args["save_source"] from playwell_rpa.data.text import create_text text = create_text(type=save_source[0], activity_id=request.activity_id, domain_id=request.domain_id, name=save_source[1], meta=save_source[2], tmp=save_source[3]) text.write(driver.page_source) return Result.ok(data={"session_id": session_id, "gathered": gathered})
def exec_cmd(request: ServiceRequestMessage): cmd = request.args["cmd"] os.system(cmd) return Result.ok()
def add(request_messages: Sequence[ServiceRequestMessage]): return [ Result.ok({"result": req.args["a"] + req.args["b"]}) for req in request_messages ]