gomyck-tools 1.3.1__py3-none-any.whl → 1.3.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 +0 -0
  2. ctools/aes_tools.py +35 -0
  3. ctools/api_result.py +55 -0
  4. ctools/application.py +386 -0
  5. ctools/b64.py +7 -0
  6. ctools/bashPath.py +13 -0
  7. ctools/bottle_web_base.py +169 -0
  8. ctools/bottle_webserver.py +143 -0
  9. ctools/bottle_websocket.py +75 -0
  10. ctools/browser_element_tools.py +314 -0
  11. ctools/call.py +71 -0
  12. ctools/cftp.py +74 -0
  13. ctools/cjson.py +54 -0
  14. ctools/ckafka.py +159 -0
  15. ctools/compile_tools.py +18 -0
  16. ctools/console.py +55 -0
  17. ctools/coord_trans.py +127 -0
  18. ctools/credis.py +111 -0
  19. ctools/cron_lite.py +252 -0
  20. ctools/ctoken.py +34 -0
  21. ctools/cword.py +30 -0
  22. ctools/czip.py +130 -0
  23. ctools/database.py +185 -0
  24. ctools/date_utils.py +43 -0
  25. ctools/dict_wrapper.py +20 -0
  26. ctools/douglas_rarefy.py +136 -0
  27. ctools/download_tools.py +57 -0
  28. ctools/enums.py +4 -0
  29. ctools/ex.py +31 -0
  30. ctools/excelOpt.py +36 -0
  31. ctools/html_soup.py +35 -0
  32. ctools/http_utils.py +24 -0
  33. ctools/images_tools.py +27 -0
  34. ctools/imgDialog.py +44 -0
  35. ctools/metrics.py +131 -0
  36. ctools/mqtt_utils.py +289 -0
  37. ctools/obj.py +20 -0
  38. ctools/pacth.py +74 -0
  39. ctools/plan_area_tools.py +97 -0
  40. ctools/process_pool.py +36 -0
  41. ctools/pty_tools.py +72 -0
  42. ctools/resource_bundle_tools.py +121 -0
  43. ctools/rsa.py +70 -0
  44. ctools/screenshot_tools.py +127 -0
  45. ctools/sign.py +20 -0
  46. ctools/sm_tools.py +49 -0
  47. ctools/snow_id.py +76 -0
  48. ctools/str_diff.py +20 -0
  49. ctools/string_tools.py +85 -0
  50. ctools/sys_info.py +157 -0
  51. ctools/sys_log.py +89 -0
  52. ctools/thread_pool.py +35 -0
  53. ctools/upload_tools.py +40 -0
  54. ctools/win_canvas.py +83 -0
  55. ctools/win_control.py +106 -0
  56. ctools/word_fill.py +562 -0
  57. ctools/word_fill_entity.py +46 -0
  58. ctools/work_path.py +69 -0
  59. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
  60. gomyck_tools-1.3.2.dist-info/RECORD +62 -0
  61. gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
  62. gomyck_tools-1.3.1.dist-info/RECORD +0 -4
  63. gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
  64. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
ctools/sys_info.py ADDED
@@ -0,0 +1,157 @@
1
+ import hashlib
2
+ import os
3
+ import platform
4
+
5
+ from ctools import cjson, sm_tools, work_path
6
+
7
+ MACHINE_KEY = b'EnrGffoorbFyTYoS0902YyT1Fhehj4InpbezIDUuPOg='
8
+
9
+ class MachineInfo:
10
+ machine_code = None
11
+
12
+ def get_user():
13
+ import getpass
14
+ return getpass.getuser()
15
+
16
+ def get_machine_code():
17
+ if MachineInfo.machine_code: return MachineInfo.machine_code
18
+ destPath = os.path.join(work_path.get_user_work_path(), "AppData/Local/machine")
19
+ machine_file = os.path.join(destPath, 'machine_code.mc')
20
+ origin_machine_code = get_origin_machine_code()
21
+ if os.path.exists(machine_file):
22
+ with open(machine_file, 'r') as f:
23
+ file_code = f.readline()
24
+ dec_code = ''
25
+ try:
26
+ dec_code = cjson.loads(sm_tools.decrypt_with_sm4(MACHINE_KEY, file_code))
27
+ origin_code = dec_code.get("origin_code")
28
+ if origin_code == origin_machine_code:
29
+ MachineInfo.machine_code = dec_code.get("hash_code")
30
+ print("use current machine code {}".format(MachineInfo.machine_code))
31
+ return MachineInfo.machine_code
32
+ except Exception:
33
+ print('machine code file is error: {} {}'.format(file_code, dec_code))
34
+ hash_machine_code = get_hash_machine_code(origin_machine_code)
35
+ machine_code = {"origin_code": origin_machine_code, "hash_code": hash_machine_code}
36
+ enc_code = sm_tools.encrypt_with_sm4(MACHINE_KEY, cjson.dumps(machine_code))
37
+ os.makedirs(destPath, exist_ok=True)
38
+ with open(machine_file, 'w') as f:
39
+ f.write(enc_code)
40
+ f.flush()
41
+ MachineInfo.machine_code = hash_machine_code
42
+ print("init new machine code {}".format(hash_machine_code))
43
+ return MachineInfo.machine_code
44
+
45
+
46
+ def get_origin_machine_code():
47
+ # 获取CPU序列号
48
+ cpu_serial = platform.processor()
49
+ # 获取硬盘序列号
50
+ disk_serial = ''
51
+ if platform.system() == 'Windows':
52
+ import ctypes
53
+ kernel32 = ctypes.windll.kernel32
54
+ volume_serial = ctypes.c_ulonglong(0)
55
+ kernel32.GetVolumeInformationW(
56
+ ctypes.c_wchar_p("C:\\"),
57
+ None,
58
+ 0,
59
+ ctypes.pointer(volume_serial),
60
+ None,
61
+ None,
62
+ None,
63
+ 0
64
+ )
65
+ disk_serial = str(volume_serial.value)
66
+ combined_info = cpu_serial + disk_serial + '-gomyck'
67
+ return hashlib.md5(combined_info.encode()).hexdigest()
68
+
69
+
70
+ def get_hash_machine_code(origin_code):
71
+ import uuid
72
+ code = origin_code + uuid.uuid1().hex
73
+ machine_code = hashlib.md5(code.encode()).hexdigest()
74
+ return machine_code.upper()
75
+
76
+ def get_public_ip():
77
+ import requests
78
+ try:
79
+ response = requests.get("https://api.ipify.org?format=json")
80
+ ip = response.json()["ip"]
81
+ return ip
82
+ except Exception as e:
83
+ return f"Failed to get public IP: {e}"
84
+
85
+ def get_local_ipv4():
86
+ import psutil
87
+ import socket
88
+ interfaces = psutil.net_if_addrs()
89
+ for interface, addresses in interfaces.items():
90
+ for address in addresses:
91
+ if address.family == socket.AF_INET and not address.address.startswith("127."):
92
+ return address.address
93
+ print("Failed to get local IPv4 address, try another way...")
94
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
95
+ try:
96
+ s.connect(("8.8.8.8", 80))
97
+ ip = s.getsockname()[0]
98
+ except Exception:
99
+ ip = '127.0.0.1'
100
+ finally:
101
+ s.close()
102
+ return ip
103
+
104
+ def get_remote_ipv4():
105
+ from bottle import request
106
+ try:
107
+ return request.remote_route[0]
108
+ except:
109
+ return '127.0.0.1'
110
+
111
+ def get_proc_pid_by(cmdline):
112
+ import psutil
113
+ """
114
+ 根据命令行信息获取进程pid
115
+ :param cmdline:
116
+ :return:
117
+ """
118
+ pid_list = []
119
+ proc_list = psutil.process_iter()
120
+ for proc in proc_list:
121
+ try:
122
+ cmdline_str = "".join(proc.cmdline())
123
+ if cmdline in cmdline_str:
124
+ pid_list.append(proc.pid)
125
+ except Exception:
126
+ pass
127
+ return pid_list
128
+
129
+ def get_os_architecture():
130
+ if '64' in platform.machine():
131
+ return '64'
132
+ else:
133
+ return '32'
134
+
135
+
136
+ def get_os_architecture4x():
137
+ if '64' in platform.machine():
138
+ return 'x64'
139
+ else:
140
+ return 'x86'
141
+
142
+
143
+ def get_os_version():
144
+ system = platform.system()
145
+ if system == "Windows":
146
+ version = platform.win32_ver()[0]
147
+ return version
148
+ else:
149
+ return "Unsupported OS"
150
+
151
+
152
+ def get_platform_name():
153
+ version = get_os_version()
154
+ if version == '7':
155
+ return 'win{}{}'.format(version, get_os_architecture4x())
156
+ else:
157
+ return 'win{}'.format(version)
ctools/sys_log.py ADDED
@@ -0,0 +1,89 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import time
5
+ import traceback
6
+
7
+ from ctools import call, work_path
8
+
9
+ clog: logging.Logger = None
10
+ flog: logging.Logger = None
11
+
12
+ neglect_keywords = [
13
+ "OPTIONS",
14
+ ]
15
+
16
+
17
+ # 文件日志
18
+ @call.once
19
+ def _file_log(sys_log_path: str = './', log_level: int = logging.INFO, mixin: bool = False) -> logging:
20
+ try:
21
+ os.mkdir(sys_log_path)
22
+ except Exception:
23
+ pass
24
+ log_file = sys_log_path + os.path.sep + "log-" + time.strftime("%Y-%m-%d-%H", time.localtime(time.time())) + ".log"
25
+ if mixin:
26
+ handlers = [logging.FileHandler(filename=log_file, encoding='utf-8'), logging.StreamHandler()]
27
+ else:
28
+ handlers = [logging.FileHandler(filename=log_file, encoding='utf-8')]
29
+ logging.basicConfig(level=log_level,
30
+ format='%(asctime)s-%(levelname)s-%(thread)d-%(module)s(%(funcName)s:%(lineno)d) %(message)s',
31
+ datefmt='%Y%m%d%H%M%S',
32
+ handlers=handlers)
33
+ logger = logging.getLogger('ck-flog')
34
+ return logger
35
+
36
+
37
+ # 控制台日志
38
+ @call.once
39
+ def _console_log(log_level: int = logging.INFO) -> logging:
40
+ handler = logging.StreamHandler()
41
+ logging.basicConfig(level=log_level,
42
+ format='%(asctime)s-%(levelname)s-%(thread)d-%(module)s(%(funcName)s:%(lineno)d) %(message)s',
43
+ datefmt='%Y%m%d%H%M%S',
44
+ handlers=[handler])
45
+ logger = logging.getLogger('ck-clog')
46
+ return logger
47
+
48
+
49
+ class GlobalLogger(object):
50
+ def __init__(self, logger):
51
+ sys.stdout = self
52
+ sys.stderr = self
53
+ sys.excepthook = self.handle_exception
54
+ self.log = logger
55
+
56
+ def write(self, message):
57
+ if message == '\n' or message == '\r\n': return
58
+ global neglect_keywords
59
+ for neglect_keyword in neglect_keywords:
60
+ if neglect_keyword in message: return
61
+ try:
62
+ stack = traceback.extract_stack(limit=3)
63
+ caller = stack[-2]
64
+ location = f"{os.path.splitext(os.path.basename(caller.filename))[0]}({caller.name}:{caller.lineno})"
65
+ self.log.info(f"{location} {message.strip()}")
66
+ except Exception:
67
+ self.log.info(message.strip())
68
+
69
+ def handle_exception(self, exc_type, exc_value, exc_traceback):
70
+ if issubclass(exc_type, KeyboardInterrupt):
71
+ sys.__excepthook__(exc_type, exc_value, exc_traceback)
72
+ return
73
+ formatted_exception = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
74
+ self.log.error(f"An error occurred:\n{formatted_exception.strip()}")
75
+
76
+ def flush(self):
77
+ pass
78
+
79
+
80
+ @call.init
81
+ def _init_log() -> None:
82
+ global flog, clog
83
+ flog = _file_log(sys_log_path='{}/ck-py-log/'.format(work_path.get_user_work_path()), mixin=True, log_level=logging.DEBUG)
84
+ clog = _console_log()
85
+ GlobalLogger(flog)
86
+
87
+ def setLevel(log_level=logging.INFO):
88
+ flog.setLevel(log_level)
89
+ clog.setLevel(log_level)
ctools/thread_pool.py ADDED
@@ -0,0 +1,35 @@
1
+ import os
2
+ import threading
3
+ import time
4
+ from concurrent.futures import ThreadPoolExecutor
5
+
6
+ from ctools import call
7
+
8
+ thread_local = threading.local()
9
+
10
+ _threadPool: ThreadPoolExecutor = None
11
+
12
+ @call.init
13
+ def init():
14
+ global _threadPool
15
+ max_work_num = min(32, (os.cpu_count() or 1) + 4) # 最多 32 个
16
+ _threadPool = ThreadPoolExecutor(max_workers= max_work_num, thread_name_prefix='ck-')
17
+
18
+ def cb(f, callback):
19
+ exc = f.exception()
20
+ if exc:
21
+ print(f"Task failed: {exc}")
22
+ if callback: callback(exc)
23
+ else:
24
+ if callback: callback(f.result())
25
+
26
+ def submit(func, *args, callback=None, **kwargs):
27
+ if _threadPool is None: raise Exception('thread pool is not init')
28
+ future = _threadPool.submit(func, *args, **kwargs)
29
+ future.add_done_callback(lambda f: cb(f, callback))
30
+ time.sleep(0.01)
31
+ return future
32
+
33
+ def shutdown(wait=True):
34
+ if _threadPool is None: raise Exception('thread pool is not init')
35
+ _threadPool.shutdown(wait=wait)
ctools/upload_tools.py ADDED
@@ -0,0 +1,40 @@
1
+ import os
2
+
3
+ from ctools import sys_log, date_utils
4
+
5
+ log = sys_log.flog
6
+
7
+ """
8
+ 文件上传服务
9
+ """
10
+
11
+
12
+ def save(upload, output_dir: str, file_name: str = None):
13
+ """
14
+ 根据上传的upload对象保存上传文件
15
+ :param output_dir:
16
+ :param upload:
17
+ :param file_name:
18
+ :return: 保存文件路径
19
+ """
20
+
21
+ # 确保文件夹存在
22
+ os.makedirs(output_dir, exist_ok=True)
23
+
24
+ unique_filename = file_name
25
+ if not unique_filename:
26
+ # 生成不重复的文件名,加上时间戳
27
+ file_name, ext = os.path.splitext(upload.raw_filename)
28
+ timestamp = date_utils.get_time()
29
+ unique_filename = f'{file_name}_{timestamp}{ext}'
30
+ else:
31
+ dot_index = unique_filename.find(".")
32
+ suffix = os.path.splitext(upload.raw_filename)[-1]
33
+ if dot_index != -1:
34
+ unique_filename = os.path.splitext(unique_filename)[0] + suffix
35
+ else:
36
+ unique_filename = unique_filename + suffix
37
+ save_path = os.path.join(output_dir, unique_filename)
38
+ upload.save(save_path, overwrite=True)
39
+ log.info("上传文件成功: %s" % save_path)
40
+ return file_name, save_path
ctools/win_canvas.py ADDED
@@ -0,0 +1,83 @@
1
+ import tkinter as tk
2
+ from multiprocessing import Process, Queue
3
+
4
+ data_queue: Queue = None
5
+ canvas_process: Process = None
6
+
7
+ class MSGType:
8
+ BOTTOM = 'bottom' #置于底部
9
+ DRAW = 'draw' #绘制矩形
10
+
11
+ class CanvasInfo:
12
+ def __init__(self,title: str='', points: list=None, msg_type: MSGType=MSGType.DRAW):
13
+ self.msg_type = msg_type
14
+ # 标题
15
+ self.title = title
16
+ # 左上, 右上, 右下, 左下
17
+ # [(100, 100), (200, 100), (200, 150), (100, 150)]
18
+ self.points = points
19
+
20
+ def on_ctrl_click(event):
21
+ print("Ctrl+Left Click detected")
22
+
23
+ class RecorderTool(tk.Tk):
24
+ def __init__(self, data: Queue):
25
+ super().__init__()
26
+ self.data = data
27
+ self.title('Recorder Tool')
28
+ self.attributes('-topmost', True)
29
+ self.overrideredirect(True)
30
+ self.attributes('-transparentcolor', 'grey')
31
+ self.geometry("{0}x{1}+0+0".format(self.winfo_screenwidth(), self.winfo_screenheight()))
32
+ self.canvas = tk.Canvas(self, highlightthickness=0, bg='grey')
33
+ self.canvas.pack(fill="both", expand=True)
34
+ self.bind('<Control-Button-1>', on_ctrl_click)
35
+
36
+ self.check_queue()
37
+
38
+ def draw_red_border(self, canvas_info: CanvasInfo):
39
+ self.clear_canvas()
40
+ points = canvas_info.points
41
+ x1, y1 = points[0]
42
+ x2, y2 = points[1]
43
+ x3, y3 = points[2]
44
+ x4, y4 = points[3]
45
+ self.canvas.create_line(x1, y1, x2, y2, fill="red", width=2)
46
+ self.canvas.create_line(x2, y2, x3, y3, fill="red", width=2)
47
+ self.canvas.create_line(x3, y3, x4, y4, fill="red", width=2)
48
+ self.canvas.create_line(x4, y4, x1, y1, fill="red", width=2)
49
+ text_x = (x1 + x2) / 2
50
+ text_y = y1 - 15
51
+ if text_y < 0: text_y = y3 + 15
52
+ self.canvas.create_text(text_x, text_y, text=canvas_info.title, font=("Arial", 12), fill="red")
53
+
54
+ def clear_canvas(self):
55
+ self.canvas.delete("all")
56
+
57
+ def check_queue(self):
58
+ if not self.data.empty():
59
+ canvas_info = self.data.get_nowait()
60
+ if canvas_info.msg_type == MSGType.BOTTOM:
61
+ self.clear_canvas()
62
+ self.withdraw()
63
+ elif canvas_info.msg_type == MSGType.DRAW:
64
+ self.deiconify()
65
+ self.draw_red_border(canvas_info)
66
+ self.after(10, self.check_queue)
67
+
68
+ def _init_recorder(data):
69
+ canvas_app = RecorderTool(data)
70
+ canvas_app.mainloop()
71
+
72
+ def start():
73
+ global data_queue, canvas_process
74
+ data_queue = Queue()
75
+ canvas_process = Process(target=_init_recorder, args=(data_queue,))
76
+ canvas_process.start()
77
+ return canvas_process
78
+
79
+ def stop():
80
+ canvas_process.terminate()
81
+ canvas_process.join()
82
+ data_queue.close()
83
+
ctools/win_control.py ADDED
@@ -0,0 +1,106 @@
1
+ import os
2
+ import time
3
+
4
+ import pyautogui
5
+ import uiautomation as auto
6
+ from pynput import keyboard
7
+
8
+ from ctools import thread_pool, win_canvas, application, string_tools
9
+
10
+ current_control = None
11
+ ctrl_pressed = False
12
+ keyboard_listener = None
13
+ control_json = {}
14
+ screenshot_path = ""
15
+
16
+ class ControlInfo:
17
+ def __init__(self, ControlType, ClassName, AutomationId, Name, Depth):
18
+ self.ControlType = ControlType
19
+ self.ClassName = ClassName
20
+ self.AutomationId = AutomationId
21
+ self.Name = Name
22
+ #self.Depth = Depth
23
+
24
+
25
+ def get_control_from_cursor():
26
+ global control_json, screenshot_path
27
+ with auto.UIAutomationInitializerInThread():
28
+ win_canvas.data_queue.put(win_canvas.CanvasInfo(msg_type=win_canvas.MSGType.BOTTOM))
29
+ time.sleep(0.2)
30
+ try:
31
+ control = auto.ControlFromCursor()
32
+ except Exception as e:
33
+ print("No control found {}".format(e))
34
+ return
35
+ if control:
36
+ #当前控件信息
37
+ global current_control
38
+ current_control = control
39
+ #绘制矩形
40
+ canvas_info = win_canvas.CanvasInfo(control.Name, [
41
+ (control.BoundingRectangle.left, control.BoundingRectangle.top),
42
+ (control.BoundingRectangle.right, control.BoundingRectangle.top),
43
+ (control.BoundingRectangle.right, control.BoundingRectangle.bottom),
44
+ (control.BoundingRectangle.left, control.BoundingRectangle.bottom)
45
+ ])
46
+ win_canvas.data_queue.put(canvas_info)
47
+ control_json = {}
48
+ c_info = ControlInfo(ControlType=current_control.ControlType, ClassName=current_control.ClassName, AutomationId=current_control.AutomationId, Name=current_control.Name, Depth=0)
49
+ _depth = 0
50
+ while current_control:
51
+ current_control = current_control.GetParentControl()
52
+ if current_control: _depth += 1
53
+ c_info.Depth = _depth
54
+ control_json.update(c_info.__dict__)
55
+
56
+ img = pyautogui.screenshot(region=[control.BoundingRectangle.left, control.BoundingRectangle.top,
57
+ control.BoundingRectangle.width(), control.BoundingRectangle.height()])
58
+ screenshot_path = os.path.join(application.Server.screenshotPath, "screenshot-%s.png" % string_tools.get_snowflake_id())
59
+ img.save(screenshot_path)
60
+ # xx = auto.Control(**control_json)
61
+ # print(control_json)
62
+ # time.sleep(2)
63
+ # xx.Click()
64
+
65
+ def on_press(key):
66
+ global ctrl_pressed, keyboard_listener
67
+ if key == keyboard.Key.ctrl_l and not ctrl_pressed:
68
+ ctrl_pressed = True
69
+ thread_pool.submit(get_control_from_cursor)
70
+ elif key == keyboard.Key.esc:
71
+ win_canvas.stop()
72
+ keyboard_listener.stop()
73
+ elif hasattr(key, 'vk') and key.vk == 192 and ctrl_pressed:
74
+ win_canvas.stop()
75
+ keyboard_listener.stop()
76
+ # pg.alert('采集成功!')
77
+
78
+ def on_release(key):
79
+ global ctrl_pressed
80
+ if key == keyboard.Key.ctrl_l:
81
+ ctrl_pressed = False
82
+ win_canvas.data_queue.put(win_canvas.CanvasInfo(msg_type=win_canvas.MSGType.BOTTOM))
83
+
84
+
85
+ def start():
86
+ global keyboard_listener
87
+ win_canvas.start()
88
+ keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release)
89
+ keyboard_listener.start()
90
+ keyboard_listener.join()
91
+
92
+
93
+ def get_control_json():
94
+ global current_control, ctrl_pressed, keyboard_listener, control_json
95
+ current_control = None
96
+ ctrl_pressed = False
97
+ keyboard_listener = None
98
+ control_json = {}
99
+ start()
100
+ if len(control_json) == 0:
101
+ time.sleep(0.5)
102
+ return control_json, screenshot_path
103
+
104
+ if __name__ == '__main__':
105
+ a = get_control_json()
106
+ print(a)