gomyck-tools 1.4.1__py3-none-any.whl → 1.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. ctools/__init__.py +21 -0
  2. ctools/ai/env_config.py +18 -1
  3. ctools/ai/llm_chat.py +8 -8
  4. ctools/ai/llm_client.py +26 -24
  5. ctools/ai/mcp/mcp_client.py +33 -17
  6. ctools/ai/tools/json_extract.py +3 -2
  7. ctools/ai/tools/quick_tools.py +68 -21
  8. ctools/ai/tools/tool_use_xml_parse.py +2 -1
  9. ctools/ai/tools/xml_extract.py +3 -0
  10. ctools/application.py +19 -17
  11. ctools/auto/browser_element.py +11 -3
  12. ctools/auto/plan_area.py +2 -2
  13. ctools/auto/pty_process.py +0 -1
  14. ctools/auto/screenshot.py +3 -4
  15. ctools/auto/win_canvas.py +10 -4
  16. ctools/auto/win_control.py +8 -4
  17. ctools/call.py +18 -22
  18. ctools/cdate.py +43 -2
  19. ctools/cid.py +3 -4
  20. ctools/cipher/aes_util.py +2 -2
  21. ctools/cipher/b64.py +2 -0
  22. ctools/cipher/czip.py +3 -1
  23. ctools/cipher/rsa.py +6 -1
  24. ctools/cipher/sign.py +1 -0
  25. ctools/cipher/sm_util.py +3 -0
  26. ctools/cjson.py +5 -0
  27. ctools/cron_lite.py +10 -4
  28. ctools/database/database.py +37 -17
  29. ctools/dict_wrapper.py +1 -0
  30. ctools/ex.py +1 -0
  31. ctools/geo/coord_trans.py +94 -94
  32. ctools/geo/douglas_rarefy.py +13 -9
  33. ctools/metrics.py +6 -0
  34. ctools/office/cword.py +7 -7
  35. ctools/office/word_fill.py +1 -4
  36. ctools/path_info.py +29 -0
  37. ctools/pools/process_pool.py +6 -1
  38. ctools/pools/thread_pool.py +6 -2
  39. ctools/similar.py +3 -0
  40. ctools/stream/ckafka.py +11 -5
  41. ctools/stream/credis.py +29 -21
  42. ctools/stream/mqtt_utils.py +2 -2
  43. ctools/sys_info.py +8 -0
  44. ctools/sys_log.py +3 -0
  45. ctools/util/cftp.py +4 -2
  46. ctools/util/http_util.py +1 -0
  47. ctools/util/image_process.py +1 -1
  48. ctools/util/snow_id.py +3 -2
  49. ctools/web/__init__.py +2 -2
  50. ctools/web/aio_web_server.py +19 -9
  51. ctools/web/api_result.py +3 -2
  52. ctools/web/bottle_web_base.py +15 -2
  53. ctools/web/bottle_webserver.py +14 -8
  54. ctools/web/bottle_websocket.py +4 -0
  55. ctools/web/ctoken.py +5 -1
  56. ctools/web/download_util.py +1 -1
  57. ctools/web/params_util.py +4 -0
  58. ctools/web/upload_util.py +1 -1
  59. {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.2.dist-info}/METADATA +2 -1
  60. gomyck_tools-1.4.2.dist-info/RECORD +82 -0
  61. gomyck_tools-1.4.1.dist-info/RECORD +0 -82
  62. {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.2.dist-info}/WHEEL +0 -0
  63. {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.2.dist-info}/licenses/LICENSE +0 -0
  64. {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.2.dist-info}/top_level.txt +0 -0
ctools/__init__.py CHANGED
@@ -0,0 +1,21 @@
1
+ import asyncio
2
+ import importlib.util
3
+
4
+ banner = """
5
+
6
+ ██████╗████████╗ ██████╗ ██████╗ ██╗ ███████╗
7
+ ██╔════╝╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██╔════╝
8
+ ██║ ██║ ██║ ██║██║ ██║██║ ███████╗
9
+ ██║ ██║ ██║ ██║██║ ██║██║ ╚════██║
10
+ ╚██████╗ ██║ ╚██████╔╝╚██████╔╝███████╗███████║
11
+ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝ --by gomyck 2025
12
+ """
13
+
14
+ print(banner)
15
+
16
+ if importlib.util.find_spec("uvloop"):
17
+ import uvloop
18
+ asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
19
+ print("uvloop version:", uvloop.__version__)
20
+ else:
21
+ print("uvloop not installed, using default asyncio loop")
ctools/ai/env_config.py CHANGED
@@ -13,7 +13,7 @@ from dotenv.main import DotEnv
13
13
  class Configuration:
14
14
  """Manages configuration and environment variables for the MCP client."""
15
15
 
16
- def __init__(self, dotenv_path: str=".env") -> None:
16
+ def __init__(self, dotenv_path: str = ".env") -> None:
17
17
  """Initialize configuration with environment variables."""
18
18
  if not os.path.exists(dotenv_path): raise FileNotFoundError(f"Could not find .env file at {dotenv_path}")
19
19
  self.env = DotEnv(dotenv_path=dotenv_path)
@@ -44,3 +44,20 @@ class Configuration:
44
44
  with open(self.get_env("MCP_CONFIG_PATH"), "r") as f:
45
45
  return json.load(f)
46
46
 
47
+ def bool_env(key, default):
48
+ value = os.getenv(key)
49
+ if value:
50
+ val = value.strip().lower()
51
+ if val == "true": return True
52
+ if val == "false": return False
53
+ return default
54
+
55
+ def float_env(key, default):
56
+ value = os.getenv(key)
57
+ if value: return float(value)
58
+ return default
59
+
60
+ def int_env(key, default):
61
+ value = os.getenv(key)
62
+ if value: return int(value)
63
+ return default
ctools/ai/llm_chat.py CHANGED
@@ -14,10 +14,11 @@ continue_prompt_default = """
14
14
  3.如果你认为所有数据都处理完毕, 请输出标记:{end_flag}.
15
15
  """
16
16
 
17
+
17
18
  class ChatSession:
18
19
 
19
20
  def __init__(self, prompts: str, llm_client: LLMClient, max_tools_call: int = 10, mcp_clients: list[MCPClient] = None,
20
- auto_complete: bool = False, end_flag: str = "EOF", continue_prompt: str = continue_prompt_default) -> None:
21
+ auto_complete: bool = False, end_flag: str = "EOF", continue_prompt: str = continue_prompt_default) -> None:
21
22
  """
22
23
  初始化聊天
23
24
  :param prompts: 提示词
@@ -49,7 +50,7 @@ class ChatSession:
49
50
  else:
50
51
  mcp_tools_prompt = await get_tools_prompt(self.mcp_clients, "")
51
52
  self.full_messages.append(build_message(ROLE.SYSTEM, mcp_tools_prompt))
52
- #log.info(mcp_tools_prompt)
53
+ # log.info(mcp_tools_prompt)
53
54
  else:
54
55
  if user_system_prompt:
55
56
  self.full_messages.append(build_message(ROLE.SYSTEM, user_system_prompt))
@@ -86,9 +87,9 @@ class ChatSession:
86
87
  await self.process_chunk_message(chunk, get_call_id, get_event_msg_func)
87
88
  llm_response = "".join(res)
88
89
  log.info("\n收到回答: %s", llm_response)
89
- no_think_llm_response = remove_think_blocks(llm_response) # 处理掉 think, 然后再判断 EOF, 避免 think 里出现 EOF
90
+ no_think_llm_response = remove_think_blocks(llm_response) # 处理掉 think, 然后再判断 EOF, 避免 think 里出现 EOF
90
91
  if self.end_flag in no_think_llm_response:
91
- self.full_messages.append(build_message(ROLE.ASSISTANT, llm_response.replace(self.end_flag, ""))) # 去掉 EOF
92
+ self.full_messages.append(build_message(ROLE.ASSISTANT, llm_response.replace(self.end_flag, ""))) # 去掉 EOF
92
93
  current_process_index = 999
93
94
  final_resp = True
94
95
  await self.process_full_message(final_resp, get_call_id, get_full_msg_func)
@@ -103,9 +104,9 @@ class ChatSession:
103
104
  self.full_messages.append(build_message(ROLE.USER, "工具调用出现错误, 请重试."))
104
105
  elif current_process_index == max_tools_call - 1:
105
106
  await self.add_tool_call_res_2_message(last_user_input, tool_call_result)
106
- self.full_messages.append(build_message(ROLE.USER, "调用次数已达上限, 请直接回答.")) # 不能调换顺序
107
+ self.full_messages.append(build_message(ROLE.USER, "调用次数已达上限, 请直接回答.")) # 不能调换顺序
107
108
  else:
108
- self.full_messages.append(build_message(ROLE.ASSISTANT, llm_response)) # 不能调换顺序
109
+ self.full_messages.append(build_message(ROLE.ASSISTANT, llm_response)) # 不能调换顺序
109
110
  await self.add_tool_call_res_2_message(last_user_input, tool_call_result)
110
111
  await self.process_tool_call_message(get_call_id, get_event_msg_func, tool_call_result)
111
112
  final_resp = False
@@ -150,7 +151,7 @@ class ChatSession:
150
151
 
151
152
  async def add_tool_call_res_2_message(self, last_user_input, tool_call_result: dict):
152
153
  if type(tool_call_result) != dict: return
153
- response:[] = tool_call_result.get("result")
154
+ response: [] = tool_call_result.get("result")
154
155
  image_content = []
155
156
  for rep in response:
156
157
  if not is_image_content(rep):
@@ -168,4 +169,3 @@ class ChatSession:
168
169
  "text": last_user_input
169
170
  })
170
171
  self.full_messages.append(build_message(ROLE.USER, image_content))
171
-
ctools/ai/llm_client.py CHANGED
@@ -4,6 +4,7 @@ import os
4
4
  import httpx
5
5
 
6
6
  from ctools import sys_log, cjson, call
7
+ from ctools.ai.env_config import float_env, bool_env, int_env
7
8
  from ctools.ai.llm_exception import LLMException
8
9
 
9
10
  logging.getLogger("httpcore").setLevel(logging.WARNING)
@@ -11,6 +12,7 @@ logging.getLogger("httpx").setLevel(logging.WARNING)
11
12
  logging.getLogger("mcp.client.sse").setLevel(logging.WARNING)
12
13
 
13
14
  log = sys_log.flog
15
+ shared_client = None
14
16
 
15
17
  def process_SSE(line):
16
18
  if not line: return None
@@ -20,8 +22,6 @@ def process_SSE(line):
20
22
  return "[DONE]"
21
23
  return data
22
24
 
23
- shared_client = None
24
-
25
25
  @call.once
26
26
  def init_shared_client():
27
27
  global shared_client
@@ -36,27 +36,27 @@ class LLMClient:
36
36
  """Manages communication with the LLM provider."""
37
37
 
38
38
  def __init__(self,
39
- api_key: str = os.getenv("LLM_API_KEY"),
40
- model_name: str = "Qwen/Qwen3-235B-A22B",
41
- temperature: float = 1,
42
- stream: bool = False,
43
- thinking: bool = True,
44
- thinking_budget: int = 4096,
45
- max_tokens: int = 4096,
46
- top_p: float = 0.5,
47
- top_k: int = 50,
48
- frequency_penalty: float = 0.5
49
- ) -> None:
50
- self.api_key = api_key
51
- self.model_name = model_name
52
- self.temperature = temperature
53
- self.stream = stream
54
- self.thinking = thinking
55
- self.thinking_budget = thinking_budget
56
- self.max_tokens = max_tokens
57
- self.top_p = top_p
58
- self.top_k = top_k
59
- self.frequency_penalty = frequency_penalty
39
+ api_key: str = "",
40
+ model_name: str = "",
41
+ temperature: float = None,
42
+ stream: bool = None,
43
+ thinking: bool = None,
44
+ thinking_budget: int = None,
45
+ max_tokens: int = None,
46
+ top_p: float = None,
47
+ top_k: int = None,
48
+ frequency_penalty: float = None
49
+ ) -> None:
50
+ self.api_key = api_key or os.getenv("LLM_API_KEY")
51
+ self.model_name = model_name or os.getenv("LLM_MODEL_NAME", "Qwen/Qwen3-235B-A22B")
52
+ self.temperature = temperature or float_env("LLM_TEMPERATURE", 0.8)
53
+ self.stream = stream or bool_env("LLM_STREAM", True)
54
+ self.thinking = thinking or bool_env("LLM_THINKING", True)
55
+ self.thinking_budget = thinking_budget or int_env("LLM_THINKING_BUDGET", 4096)
56
+ self.max_tokens = max_tokens or int_env("LLM_MAX_TOKENS", 4096)
57
+ self.top_p = top_p or float_env("LLM_TOP_P", 0.5)
58
+ self.top_k = top_k or int_env("LLM_TOP_K", 50)
59
+ self.frequency_penalty = frequency_penalty or float_env("LLM_FREQUENCY_PENALTY", 0)
60
60
  init_shared_client()
61
61
 
62
62
  async def model_completion(self, messages: list[dict[str, str]]):
@@ -112,6 +112,8 @@ class LLMClient:
112
112
  except Exception as e:
113
113
  error_message = f"Error getting LLM response: {str(e)}"
114
114
  log.error(error_message)
115
+ if isinstance(e, httpx.ReadTimeout):
116
+ raise LLMException(code=500, message="模型访问超时")
115
117
  if isinstance(e, httpx.HTTPStatusError):
116
118
  log.error(f"Status code: {e.response.status_code}")
117
119
  log.error(f"Response details: {e.response.text}")
@@ -121,7 +123,7 @@ class LLMClient:
121
123
  def no_think_compatible(self, messages):
122
124
  if not self.thinking and "qwen3" in self.model_name.lower():
123
125
  for msg in messages:
124
- if msg.get("role") in ("user", "system") and "/no_think" not in msg.get("content", ""):
126
+ if msg and msg.get("role") in ("user", "system") and "/no_think" not in msg.get("content", ""):
125
127
  msg["content"] += " /no_think"
126
128
 
127
129
  async def voice_2_text(self, file: bytes = None, file_path: str = None):
@@ -16,7 +16,7 @@ from mcp.client.stdio import stdio_client
16
16
  from mcp.client.streamable_http import streamablehttp_client
17
17
  from mcp.types import CallToolResult, TextContent, ImageContent
18
18
 
19
- from ctools import sys_log
19
+ from ctools import sys_log, cdate
20
20
  from ctools.ai.tools.tool_use_xml_parse import parse_tool_use
21
21
 
22
22
  log = sys_log.flog
@@ -66,6 +66,9 @@ Here are the rules you should always follow to solve your task:
66
66
  # User Instructions
67
67
  {user_system_prompt}
68
68
 
69
+ # Current time
70
+ {current_time}
71
+
69
72
  Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000.
70
73
  """
71
74
 
@@ -115,12 +118,14 @@ User: {
115
118
  Assistant: The population of Shanghai is 26 million, while Guangzhou has a population of 15 million. Therefore, Shanghai has the highest population.
116
119
  """
117
120
 
121
+
118
122
  async def get_tools_prompt(mcp_clients, user_system_prompt) -> str:
119
123
  all_tools = []
120
124
  for client in mcp_clients:
121
125
  tools = await client.list_server_tools()
122
126
  all_tools.extend(tools)
123
- return sys_prompt_4_mcp.format(tools_description="\n".join([tool.format_for_llm() for tool in all_tools]), user_system_prompt=user_system_prompt)
127
+ return sys_prompt_4_mcp.format(tools_description="\n".join([tool.format_for_llm() for tool in all_tools]), user_system_prompt=user_system_prompt, current_time=cdate.get_date())
128
+
124
129
 
125
130
  class Tool:
126
131
 
@@ -144,6 +149,7 @@ Args_Info:
144
149
  {chr(10).join(args_desc)}
145
150
  """
146
151
 
152
+
147
153
  class MCPClient:
148
154
 
149
155
  def __init__(self, name: str, config: dict[str, Any]) -> None:
@@ -273,6 +279,7 @@ async def mcp_tool_call(mcp_clients: MCPClient, xml_info: str) -> str:
273
279
  final_result["result"] = [error_msg]
274
280
  return final_result
275
281
 
282
+
276
283
  def res_has_img(llm_response) -> bool:
277
284
  if type(llm_response) == str: return False
278
285
  response: [] = llm_response.get("result")
@@ -281,6 +288,7 @@ def res_has_img(llm_response) -> bool:
281
288
  return True
282
289
  return False
283
290
 
291
+
284
292
  def is_image_content(content: dict) -> bool:
285
293
  try:
286
294
  if content.get("mime_type") and content.get("data"):
@@ -289,22 +297,30 @@ def is_image_content(content: dict) -> bool:
289
297
  except Exception:
290
298
  return False
291
299
 
300
+
301
+ from contextlib import asynccontextmanager
302
+
292
303
  @asynccontextmanager
293
304
  async def init_mcp_clients(mcp_server_config: dict[str, Any]) -> list[MCPClient]:
294
305
  mcp_clients = []
295
- if not mcp_server_config["mcpServers"]: yield mcp_clients
296
- for name, sc in mcp_server_config["mcpServers"].items():
297
- try:
298
- mc = MCPClient(name, sc)
299
- await mc.initialize()
300
- mcp_clients.append(mc)
301
- except Exception as e:
302
- log.exception(f"Error initializing MCP servers: {e}")
303
- yield mcp_clients
304
- for client in mcp_clients:
305
- try:
306
- print(client.name)
307
- await client.cleanup()
308
- except Exception as e:
309
- log.exception(f"Error unloading MCP servers: {e}")
306
+ if not mcp_server_config["mcpServers"]:
307
+ yield mcp_clients
308
+ return # 这句必须有,避免进入 finally
309
+ try:
310
+ for name, sc in mcp_server_config["mcpServers"].items():
311
+ try:
312
+ mc = MCPClient(name, sc)
313
+ await mc.initialize()
314
+ mcp_clients.append(mc)
315
+ except Exception as e:
316
+ log.exception(f"Error initializing MCP server [{name}]: {e}")
317
+ yield mcp_clients # 只允许有一个 yield
318
+ finally:
319
+ for client in mcp_clients:
320
+ try:
321
+ print(client.name)
322
+ await client.cleanup()
323
+ except* Exception as eg:
324
+ #log.warning(f"ExceptionGroup unloading MCP server [{client.name}]: {eg}")
325
+ pass
310
326
 
@@ -47,8 +47,9 @@ def extract_json_from_text(text):
47
47
 
48
48
  return None
49
49
 
50
+
50
51
  if __name__ == '__main__':
51
- xx = """
52
+ xx = """
52
53
  <think>
53
54
 
54
55
  </think>
@@ -198,4 +199,4 @@ if __name__ == '__main__':
198
199
 
199
200
  """
200
201
 
201
- print((cjson.dumps(extract_json_from_text(xx))))
202
+ print((cjson.dumps(extract_json_from_text(xx))))
@@ -4,25 +4,49 @@ __author__ = 'haoyang'
4
4
  __date__ = '2025/6/9 09:49'
5
5
 
6
6
  import asyncio
7
+ import base64
7
8
  import json
8
- import os
9
+ import mimetypes
9
10
  import sys
10
11
  import uuid
11
12
 
12
- from aiohttp import web
13
+ from ctools.web.aio_web_server import get_stream_resp
13
14
 
14
- import base64
15
- import mimetypes
16
15
 
17
16
  class ROLE:
18
17
  ASSISTANT = "assistant"
19
18
  USER = "user"
20
19
  SYSTEM = "system"
21
20
 
21
+
22
22
  def build_message(role_type: ROLE, content):
23
+ """
24
+ 快速构建消息
25
+ Parameters
26
+ ----------
27
+ role_type 消息类型
28
+ content 消息内容
29
+
30
+ Returns 消息
31
+ -------
32
+
33
+ """
23
34
  return {"role": role_type, "content": content}
24
35
 
25
- def build_image_message(content: str, file: bytes=None, file_path: str=None):
36
+
37
+ def build_image_message(content: str, file: bytes = None, file_path: str = None):
38
+ """
39
+ 快速构建图片消息
40
+ Parameters
41
+ ----------
42
+ content 问题内容
43
+ file 图片文件
44
+ file_path 图片文件路径
45
+
46
+ Returns 消息
47
+ -------
48
+
49
+ """
26
50
  rep = _get_image_data_and_mime(file, file_path)
27
51
  img_content = [{
28
52
  "type": "image_url",
@@ -35,33 +59,56 @@ def build_image_message(content: str, file: bytes=None, file_path: str=None):
35
59
  }]
36
60
  return build_message(ROLE.USER, img_content)
37
61
 
38
- def build_call_back(debug=False):
62
+
63
+ async def build_call_back(debug=False, request=None, SSE=True):
64
+ """
65
+ 快速构建回调函数
66
+ Parameters
67
+ ----------
68
+ debug 是否开启调试
69
+ request http请求
70
+
71
+ Returns 响应对象, 消息队列, 回调函数
72
+ -------
73
+ """
74
+ response = None
75
+ if request: response = await get_stream_resp(request)
39
76
  call_id = uuid.uuid4()
40
- queue = asyncio.Queue()
77
+ message_queue = asyncio.Queue()
78
+
41
79
  async def on_msg(cid, role, msg):
80
+ nonlocal response
42
81
  if debug: print(msg, file=sys.__stdout__, end='', flush=True)
43
- await queue.put({"id": cid, "role": role, "msg": msg})
82
+ final_msg = {"id": cid, "role": role, "msg": msg}
83
+ await message_queue.put(msg)
84
+ if response:
85
+ if SSE:
86
+ await response.write(f"data: {json.dumps(final_msg)}\n\n".encode("utf-8"))
87
+ else:
88
+ await response.write(msg.encode("utf-8"))
89
+
44
90
  async def on_final(cid, is_final, msg):
91
+ nonlocal response
45
92
  if debug: print(cid, is_final, msg, file=sys.__stdout__, flush=True)
46
93
  if is_final:
47
- await queue.put("[DONE]")
94
+ await message_queue.put("[DONE]")
95
+ if response:
96
+ if SSE: await response.write(b"data: [DONE]\n\n")
97
+ await response.write_eof()
48
98
  else:
49
99
  nonlocal call_id
50
100
  call_id = uuid.uuid4()
101
+
51
102
  def get_call_id():
52
103
  return call_id.hex
53
- async def process_sse_resp(response: web.StreamResponse, e: Exception = None):
54
- if e:
55
- await response.write(b"data: " + f'{{"code": 500, "error": "{e}"}}'.encode('utf-8') + b"\n\n")
56
- await response.write(b"data: [DONE]\n\n")
57
- return
58
- while True:
59
- msg = await queue.get()
60
- if msg == "[DONE]":
61
- await response.write(b"data: [DONE]\n\n")
62
- break
63
- await response.write(f"data: {json.dumps(msg)}\n\n".encode("utf-8"))
64
- return process_sse_resp, {"get_call_id": get_call_id, "get_event_msg_func": on_msg, "get_full_msg_func": on_final}
104
+
105
+ return response, message_queue, {"get_call_id": get_call_id, "get_event_msg_func": on_msg, "get_full_msg_func": on_final}
106
+
107
+
108
+
109
+
110
+
111
+
65
112
 
66
113
 
67
114
 
@@ -1,6 +1,6 @@
1
+ import html
1
2
  import json
2
3
  import xml.etree.ElementTree as ET
3
- import html
4
4
 
5
5
 
6
6
  def parse_tool_use(xml_string):
@@ -26,6 +26,7 @@ def parse_tool_use(xml_string):
26
26
  except Exception as e:
27
27
  raise ValueError(f"tool_use_{tool_name} 解析失败: {e}")
28
28
 
29
+
29
30
  # 测试
30
31
  if __name__ == '__main__':
31
32
  xml_input = """
@@ -1,7 +1,10 @@
1
1
  import re
2
2
 
3
+
3
4
  def extract_all_xml_blocks(text):
4
5
  return re.findall(r"<tool_use>.*?</tool_use>", text, re.DOTALL)
6
+
7
+
5
8
  text = """
6
9
  一些内容...
7
10
  123
ctools/application.py CHANGED
@@ -39,9 +39,9 @@ class Authorization:
39
39
 
40
40
 
41
41
  class Database:
42
- url_biz = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core", 'systemRDB') # 数据库文件地址
43
- url_func = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core", 'explorerSearch') # 函数库地址
44
- url_auth = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core", 'tmp') # 授权库地址
42
+ url_biz = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core", 'systemRDB') # 数据库文件地址
43
+ url_func = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core", 'explorerSearch') # 函数库地址
44
+ url_auth = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core", 'tmp') # 授权库地址
45
45
  pool_size = 5
46
46
  max_overflow = 25
47
47
 
@@ -54,7 +54,7 @@ class Upload:
54
54
 
55
55
 
56
56
  class Schedule:
57
- rpa_script_path = os.path.join(path_info.get_user_path_info(), ".cache/.tmp") # 执行流程存储目录
57
+ rpa_script_path = os.path.join(path_info.get_user_temp_path(), ".cache/.tmp") # 执行流程存储目录
58
58
  template_output_path = os.path.join(Server.baseWorkPath, 'document') # 使用模板生成的文档存储目录
59
59
  play_wright_slow_mo = 0 # play wright每一步执行延时时间, 单位毫秒
60
60
 
@@ -71,7 +71,7 @@ def sync_config():
71
71
  server_conf = conf['Server']
72
72
  Server.version = server_conf['version']
73
73
  Server.port = int(server_conf['port'])
74
- Server.baseWorkPath = path_info.get_user_path_info(server_conf['baseWorkPath'])
74
+ Server.baseWorkPath = path_info.get_user_work_path(server_conf['baseWorkPath'])
75
75
  Server.sysLogPath = os.path.join(Server.baseWorkPath, ".logs")
76
76
  ######### Server 必须放在第一个初始化的位置 ########
77
77
  if "Database" in sections:
@@ -108,15 +108,16 @@ def sync_config():
108
108
 
109
109
 
110
110
  def check_database():
111
- db_file = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core", 'systemRDB')
111
+ db_file = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core", 'systemRDB')
112
112
  if os.path.exists(db_file):
113
113
  db_size = os.path.getsize(db_file)
114
114
  if db_size < 2000 * 1024: os.remove(db_file)
115
- db_file = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core", 'tmp')
115
+ db_file = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core", 'tmp')
116
116
  if os.path.exists(db_file):
117
117
  db_size = os.path.getsize(db_file)
118
118
  if db_size < 2000 * 1024: os.remove(db_file)
119
119
 
120
+
120
121
  def validate_sign():
121
122
  sign_app_val = sign.generate_signature(path_info.get_install_path('rpa-server.exe'))
122
123
  with open(path_info.get_install_path('rpa-server.sign')) as sign_file:
@@ -129,8 +130,8 @@ def path_config():
129
130
  print('start config path')
130
131
  driverPath = Upload.driver_path
131
132
  pythonPath = os.path.join(path_info.get_Users_path(), 'Public/python-3.8.9')
132
- hplPath = os.path.join(path_info.get_user_path_info(), 'AppData/Local/ms-playwright')
133
- taguiPath = os.path.join(path_info.get_user_path_info(), 'AppData/Roaming/tagui')
133
+ hplPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Local/ms-playwright')
134
+ taguiPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Roaming/tagui')
134
135
  os.environ['PATH'] = os.pathsep.join([pythonPath, os.path.join(pythonPath, 'Scripts'), driverPath, os.path.join(hplPath, 'chromium-920619\chrome-win'), taguiPath, os.environ['PATH']])
135
136
  print('end config path')
136
137
 
@@ -139,8 +140,8 @@ def sync_version(callFunc):
139
140
  destFilePath = os.path.join(Server.baseWorkPath, "version")
140
141
  driverPath = Upload.driver_path
141
142
  pythonPath = os.path.join(path_info.get_Users_path(), 'Public/python-3.8.9')
142
- msPlayPath = os.path.join(path_info.get_user_path_info(), 'AppData/Local/ms-playwright')
143
- taguiPath = os.path.join(path_info.get_user_path_info(), 'AppData/Roaming/tagui')
143
+ msPlayPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Local/ms-playwright')
144
+ taguiPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Roaming/tagui')
144
145
  if not os.path.exists(destFilePath):
145
146
  try:
146
147
  shutil.rmtree(pythonPath)
@@ -186,7 +187,7 @@ def sync_version(callFunc):
186
187
  def sync_database():
187
188
  for db in [['simpleYWI', 'systemRDB', False], ['simpleHSI', 'explorerSearch', True], ['simpleSQI', 'tmp', False]]:
188
189
  dbPath = os.path.join(path_info.get_app_path(), "extension", db[0])
189
- destDBPath = os.path.join(path_info.get_user_path_info(), "AppData/Local/SystemWin32Core")
190
+ destDBPath = os.path.join(path_info.get_user_temp_path(), "AppData/Local/SystemWin32Core")
190
191
  os.makedirs(destDBPath, exist_ok=True)
191
192
  destFilePath = os.path.join(destDBPath, db[1])
192
193
  try:
@@ -318,8 +319,8 @@ def sync_driver():
318
319
  def sync_tagui():
319
320
  print('HTG ENGINE 开始安装...')
320
321
  plPath = os.path.join(path_info.get_install_path(), "sources", 'htg-source.zip')
321
- destPLPath = os.path.join(path_info.get_user_path_info(), 'AppData/Roaming/tagui')
322
- unzipPLPath = os.path.join(path_info.get_user_path_info(), 'AppData/Roaming/')
322
+ destPLPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Roaming/tagui')
323
+ unzipPLPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Roaming/')
323
324
  if not os.path.exists(os.path.join(destPLPath)):
324
325
  with zipfile.ZipFile(plPath, 'r') as zip_ref:
325
326
  zip_ref.extractall(unzipPLPath)
@@ -331,8 +332,8 @@ def sync_tagui():
331
332
  def sync_playwright():
332
333
  print('HPL ENGINE 开始安装...')
333
334
  plPath = os.path.join(path_info.get_install_path(), "sources", 'ms-playwright.zip')
334
- destPLPath = os.path.join(path_info.get_user_path_info(), 'AppData/Local/ms-playwright')
335
- unzipPLPath = os.path.join(path_info.get_user_path_info(), 'AppData/Local/')
335
+ destPLPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Local/ms-playwright')
336
+ unzipPLPath = os.path.join(path_info.get_user_temp_path(), 'AppData/Local/')
336
337
  if not os.path.exists(os.path.join(destPLPath)):
337
338
  with zipfile.ZipFile(plPath, 'r') as zip_ref:
338
339
  zip_ref.extractall(unzipPLPath)
@@ -340,9 +341,10 @@ def sync_playwright():
340
341
  else:
341
342
  print('HPL ENGINE 依赖安装成功(1)')
342
343
 
344
+
343
345
  def sync_paddleocr_model():
344
346
  model_path = os.path.join(path_info.get_install_path(), "sources", 'paddleocr-model.zip')
345
- paddleocr_path = path_info.get_user_path_info(".paddleocr")
347
+ paddleocr_path = path_info.get_user_temp_path(".paddleocr")
346
348
  if not os.path.exists(paddleocr_path) and os.path.exists(model_path) and get_os_architecture() == "64":
347
349
  print('安装paddleocr模型文件开始')
348
350
  with zipfile.ZipFile(model_path, 'r') as zip_ref:
@@ -23,6 +23,7 @@ g_result = []
23
23
  g_quite_flag = False
24
24
  picture_path = ""
25
25
 
26
+
26
27
  def get_hovered_element_html(url, explore, callback):
27
28
  global g_driver, g_callback, g_result
28
29
  g_callback = callback
@@ -73,10 +74,10 @@ def get_hovered_element_html(url, explore, callback):
73
74
  match_dom = page_dom_tree.xpath('//*[@ck-flag="ck"]')
74
75
  for ck_element in match_dom:
75
76
  ck_xpath = page_dom_tree.getpath(ck_element)
76
- #print('XPATH IS {}'.format(ck_xpath))
77
+ # print('XPATH IS {}'.format(ck_xpath))
77
78
  try:
78
79
  ele = driver.find_element(By.XPATH, ck_xpath)
79
- #print('XPATH_HTML: {}'.format(ele.get_attribute('outerHTML')))
80
+ # print('XPATH_HTML: {}'.format(ele.get_attribute('outerHTML')))
80
81
  except Exception:
81
82
  pass
82
83
  g_result.append(ck_xpath)
@@ -99,10 +100,12 @@ def get_hovered_element_html(url, explore, callback):
99
100
  except Exception as e:
100
101
  pass
101
102
 
103
+
102
104
  def break_func():
103
105
  keyboard_listener.stop()
104
106
  g_callback(None)
105
107
 
108
+
106
109
  def on_press(key):
107
110
  global keyboard_listener, ctrl_pressed, g_driver, g_quite_flag, picture_path
108
111
  if key == keyboard.Key.ctrl_l and not ctrl_pressed:
@@ -117,7 +120,7 @@ def on_press(key):
117
120
  if g_result:
118
121
  try:
119
122
  select_element = g_driver.find_element(By.XPATH, g_result[0])
120
- picture_path = "%s.png" % os.path.join(application.Server.screenshotPath, "element-"+cid.get_uuid())
123
+ picture_path = "%s.png" % os.path.join(application.Server.screenshotPath, "element-" + cid.get_uuid())
121
124
  select_element.screenshot(picture_path)
122
125
  except Exception:
123
126
  pass
@@ -126,11 +129,13 @@ def on_press(key):
126
129
  g_quite_flag = True
127
130
  g_callback(g_result)
128
131
 
132
+
129
133
  def on_release(key):
130
134
  global ctrl_pressed
131
135
  if key == keyboard.Key.ctrl_l:
132
136
  ctrl_pressed = False
133
137
 
138
+
134
139
  def start_keyboard_listener():
135
140
  global keyboard_listener
136
141
  keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release)
@@ -183,6 +188,7 @@ if (shade) {
183
188
  }
184
189
  """
185
190
 
191
+
186
192
  class IE:
187
193
 
188
194
  @staticmethod
@@ -287,6 +293,7 @@ class Chrome:
287
293
  def callback(xpath):
288
294
  print("Hovered Element XPATH IS :", xpath)
289
295
 
296
+
290
297
  def get_element(url: str = None, explore: str = "chrome"):
291
298
  global keyboard_listener, ctrl_pressed, g_driver, g_callback, g_result, g_quite_flag, picture_path
292
299
  keyboard_listener = None
@@ -309,6 +316,7 @@ def get_element(url: str = None, explore: str = "chrome"):
309
316
 
310
317
  return g_result, picture_path
311
318
 
319
+
312
320
  if __name__ == "__main__":
313
321
  # from explore_record_core import get_hovered_element_html, Chrome
314
322
  g_result, picture_path = get_element("weibo.com")