gomyck-tools 1.4.1__py3-none-any.whl → 1.4.3__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.
- ctools/__init__.py +21 -0
- ctools/ai/env_config.py +18 -1
- ctools/ai/llm_chat.py +8 -8
- ctools/ai/llm_client.py +26 -24
- ctools/ai/mcp/mcp_client.py +33 -17
- ctools/ai/tools/json_extract.py +3 -2
- ctools/ai/tools/quick_tools.py +71 -22
- ctools/ai/tools/tool_use_xml_parse.py +2 -1
- ctools/ai/tools/xml_extract.py +3 -0
- ctools/application.py +21 -19
- ctools/aspect.py +65 -0
- ctools/auto/browser_element.py +11 -3
- ctools/auto/plan_area.py +2 -2
- ctools/auto/pty_process.py +0 -1
- ctools/auto/screenshot.py +3 -4
- ctools/auto/win_canvas.py +10 -4
- ctools/auto/win_control.py +8 -4
- ctools/call.py +32 -47
- ctools/cdate.py +43 -2
- ctools/cid.py +6 -4
- ctools/cipher/aes_util.py +2 -2
- ctools/cipher/b64.py +2 -0
- ctools/cipher/czip.py +3 -1
- ctools/cipher/rsa.py +6 -1
- ctools/cipher/sign.py +1 -0
- ctools/cipher/sm_util.py +3 -0
- ctools/cjson.py +5 -0
- ctools/cron_lite.py +10 -4
- ctools/database/database.py +52 -22
- ctools/dict_wrapper.py +1 -0
- ctools/ex.py +4 -0
- ctools/geo/coord_trans.py +94 -94
- ctools/geo/douglas_rarefy.py +13 -9
- ctools/metrics.py +6 -0
- ctools/office/cword.py +7 -7
- ctools/office/word_fill.py +1 -4
- ctools/patch.py +88 -0
- ctools/path_info.py +29 -0
- ctools/pkg/__init__.py +4 -0
- ctools/pkg/dynamic_imp.py +38 -0
- ctools/pools/process_pool.py +6 -1
- ctools/pools/thread_pool.py +6 -2
- ctools/similar.py +3 -0
- ctools/stream/ckafka.py +11 -5
- ctools/stream/credis.py +37 -23
- ctools/stream/mqtt_utils.py +2 -2
- ctools/sys_info.py +8 -0
- ctools/sys_log.py +4 -1
- ctools/util/cftp.py +4 -2
- ctools/util/cklock.py +118 -0
- ctools/util/config_util.py +52 -0
- ctools/util/http_util.py +1 -0
- ctools/util/image_process.py +8 -0
- ctools/util/jb_cut.py +53 -0
- ctools/util/snow_id.py +3 -2
- ctools/web/__init__.py +2 -2
- ctools/web/aio_web_server.py +19 -9
- ctools/web/api_result.py +3 -2
- ctools/web/bottle_web_base.py +134 -70
- ctools/web/bottle_webserver.py +41 -35
- ctools/web/bottle_websocket.py +4 -0
- ctools/web/ctoken.py +81 -13
- ctools/web/download_util.py +1 -1
- ctools/web/params_util.py +4 -0
- ctools/web/upload_util.py +1 -1
- {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.3.dist-info}/METADATA +9 -11
- gomyck_tools-1.4.3.dist-info/RECORD +88 -0
- ctools/auto/pacth.py +0 -74
- gomyck_tools-1.4.1.dist-info/RECORD +0 -82
- {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.3.dist-info}/WHEEL +0 -0
- {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.3.dist-info}/licenses/LICENSE +0 -0
- {gomyck_tools-1.4.1.dist-info → gomyck_tools-1.4.3.dist-info}/top_level.txt +0 -0
ctools/aspect.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__author__ = 'haoyang'
|
|
4
|
+
__date__ = '2025/7/23 08:33'
|
|
5
|
+
|
|
6
|
+
import functools
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from ctools.pools import thread_pool
|
|
10
|
+
|
|
11
|
+
def _ensure_list(funcs):
|
|
12
|
+
if callable(funcs):
|
|
13
|
+
return [funcs]
|
|
14
|
+
if isinstance(funcs, (list, tuple, set)):
|
|
15
|
+
return list(funcs)
|
|
16
|
+
raise TypeError("必须是可调用对象或可迭代对象")
|
|
17
|
+
|
|
18
|
+
def before(before_funcs):
|
|
19
|
+
"""
|
|
20
|
+
用于将无参函数注入目标函数的调用前
|
|
21
|
+
支持多个函数
|
|
22
|
+
"""
|
|
23
|
+
before_funcs = _ensure_list(before_funcs)
|
|
24
|
+
def decorator(func, sync=True):
|
|
25
|
+
@functools.wraps(func)
|
|
26
|
+
def wrapper(*args, **kwargs):
|
|
27
|
+
for bf in before_funcs:
|
|
28
|
+
if callable(bf):
|
|
29
|
+
if sync:
|
|
30
|
+
bf()
|
|
31
|
+
else:
|
|
32
|
+
thread_pool.submit(bf)
|
|
33
|
+
return func(*args, **kwargs)
|
|
34
|
+
_replace_func_binding(func, wrapper)
|
|
35
|
+
return wrapper
|
|
36
|
+
return decorator
|
|
37
|
+
|
|
38
|
+
def after(after_funcs, sync=True):
|
|
39
|
+
"""
|
|
40
|
+
用于将无参函数注入目标函数的调用后
|
|
41
|
+
支持多个函数
|
|
42
|
+
"""
|
|
43
|
+
after_funcs = _ensure_list(after_funcs)
|
|
44
|
+
def decorator(func):
|
|
45
|
+
@functools.wraps(func)
|
|
46
|
+
def wrapper(*args, **kwargs):
|
|
47
|
+
result = func(*args, **kwargs)
|
|
48
|
+
for af in after_funcs:
|
|
49
|
+
if callable(af):
|
|
50
|
+
if sync:
|
|
51
|
+
af()
|
|
52
|
+
else:
|
|
53
|
+
thread_pool.submit(af)
|
|
54
|
+
return result
|
|
55
|
+
_replace_func_binding(func, wrapper)
|
|
56
|
+
return wrapper
|
|
57
|
+
return decorator
|
|
58
|
+
|
|
59
|
+
def _replace_func_binding(old_func, new_func):
|
|
60
|
+
"""
|
|
61
|
+
替换函数在其模块中的绑定,确保所有使用点都生效
|
|
62
|
+
"""
|
|
63
|
+
mod = sys.modules.get(old_func.__module__)
|
|
64
|
+
if mod and hasattr(mod, old_func.__name__):
|
|
65
|
+
setattr(mod, old_func.__name__, new_func)
|
ctools/auto/browser_element.py
CHANGED
|
@@ -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")
|
ctools/auto/plan_area.py
CHANGED
|
@@ -63,8 +63,8 @@ class PlanAreaTools:
|
|
|
63
63
|
|
|
64
64
|
def mouse_left_move(self, event):
|
|
65
65
|
self.x, self.y = event.x, event.y
|
|
66
|
-
self.xcenter = self.xstart + int((self.x-self.xstart)/2)-2
|
|
67
|
-
self.ycenter = self.ystart + int((self.y-self.ystart)/2)-2
|
|
66
|
+
self.xcenter = self.xstart + int((self.x - self.xstart) / 2) - 2
|
|
67
|
+
self.ycenter = self.ystart + int((self.y - self.ystart) / 2) - 2
|
|
68
68
|
|
|
69
69
|
self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
|
|
70
70
|
self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
|
ctools/auto/pty_process.py
CHANGED
ctools/auto/screenshot.py
CHANGED
|
@@ -65,7 +65,7 @@ class ScreenshotTools:
|
|
|
65
65
|
def change_center_point(self, event):
|
|
66
66
|
offset_x = event.x + self.xstart - self.xcenter - 2
|
|
67
67
|
offset_y = event.y + self.ystart - self.ycenter - 2
|
|
68
|
-
self.center_div.geometry(f"+{self.xstart+event.x-2}+{self.ystart+event.y-2}")
|
|
68
|
+
self.center_div.geometry(f"+{self.xstart + event.x - 2}+{self.ystart + event.y - 2}")
|
|
69
69
|
self.center_offset = (offset_x, offset_y)
|
|
70
70
|
|
|
71
71
|
def mouse_left_down(self, event):
|
|
@@ -86,8 +86,8 @@ class ScreenshotTools:
|
|
|
86
86
|
|
|
87
87
|
def mouse_left_move(self, event):
|
|
88
88
|
self.x, self.y = event.x, event.y
|
|
89
|
-
self.xcenter = self.xstart + int((self.x-self.xstart)/2)-2
|
|
90
|
-
self.ycenter = self.ystart + int((self.y-self.ystart)/2)-2
|
|
89
|
+
self.xcenter = self.xstart + int((self.x - self.xstart) / 2) - 2
|
|
90
|
+
self.ycenter = self.ystart + int((self.y - self.ystart) / 2) - 2
|
|
91
91
|
|
|
92
92
|
self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
|
|
93
93
|
self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
|
|
@@ -125,4 +125,3 @@ def screenshot():
|
|
|
125
125
|
st = ScreenshotTools()
|
|
126
126
|
st.start()
|
|
127
127
|
return st.screenshot_path, st.center_offset
|
|
128
|
-
|
ctools/auto/win_canvas.py
CHANGED
|
@@ -4,12 +4,14 @@ from multiprocessing import Process, Queue
|
|
|
4
4
|
data_queue: Queue = None
|
|
5
5
|
canvas_process: Process = None
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
class MSGType:
|
|
8
|
-
BOTTOM = 'bottom'
|
|
9
|
-
DRAW = 'draw'
|
|
9
|
+
BOTTOM = 'bottom' # 置于底部
|
|
10
|
+
DRAW = 'draw' # 绘制矩形
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
class CanvasInfo:
|
|
12
|
-
def __init__(self,title: str='', points: list=None, msg_type: MSGType=MSGType.DRAW):
|
|
14
|
+
def __init__(self, title: str = '', points: list = None, msg_type: MSGType = MSGType.DRAW):
|
|
13
15
|
self.msg_type = msg_type
|
|
14
16
|
# 标题
|
|
15
17
|
self.title = title
|
|
@@ -17,9 +19,11 @@ class CanvasInfo:
|
|
|
17
19
|
# [(100, 100), (200, 100), (200, 150), (100, 150)]
|
|
18
20
|
self.points = points
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
def on_ctrl_click(event):
|
|
21
24
|
print("Ctrl+Left Click detected")
|
|
22
25
|
|
|
26
|
+
|
|
23
27
|
class RecorderTool(tk.Tk):
|
|
24
28
|
def __init__(self, data: Queue):
|
|
25
29
|
super().__init__()
|
|
@@ -65,10 +69,12 @@ class RecorderTool(tk.Tk):
|
|
|
65
69
|
self.draw_red_border(canvas_info)
|
|
66
70
|
self.after(10, self.check_queue)
|
|
67
71
|
|
|
72
|
+
|
|
68
73
|
def _init_recorder(data):
|
|
69
74
|
canvas_app = RecorderTool(data)
|
|
70
75
|
canvas_app.mainloop()
|
|
71
76
|
|
|
77
|
+
|
|
72
78
|
def start():
|
|
73
79
|
global data_queue, canvas_process
|
|
74
80
|
data_queue = Queue()
|
|
@@ -76,8 +82,8 @@ def start():
|
|
|
76
82
|
canvas_process.start()
|
|
77
83
|
return canvas_process
|
|
78
84
|
|
|
85
|
+
|
|
79
86
|
def stop():
|
|
80
87
|
canvas_process.terminate()
|
|
81
88
|
canvas_process.join()
|
|
82
89
|
data_queue.close()
|
|
83
|
-
|
ctools/auto/win_control.py
CHANGED
|
@@ -6,9 +6,9 @@ import uiautomation as auto
|
|
|
6
6
|
from pynput import keyboard
|
|
7
7
|
|
|
8
8
|
from ctools import application
|
|
9
|
+
from ctools import cid
|
|
9
10
|
from ctools.auto import win_canvas
|
|
10
11
|
from ctools.pools import thread_pool
|
|
11
|
-
from ctools import cid
|
|
12
12
|
|
|
13
13
|
current_control = None
|
|
14
14
|
ctrl_pressed = False
|
|
@@ -16,13 +16,14 @@ keyboard_listener = None
|
|
|
16
16
|
control_json = {}
|
|
17
17
|
screenshot_path = ""
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
class ControlInfo:
|
|
20
21
|
def __init__(self, ControlType, ClassName, AutomationId, Name, Depth):
|
|
21
22
|
self.ControlType = ControlType
|
|
22
23
|
self.ClassName = ClassName
|
|
23
24
|
self.AutomationId = AutomationId
|
|
24
25
|
self.Name = Name
|
|
25
|
-
#self.Depth = Depth
|
|
26
|
+
# self.Depth = Depth
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def get_control_from_cursor():
|
|
@@ -36,10 +37,10 @@ def get_control_from_cursor():
|
|
|
36
37
|
print("No control found {}".format(e))
|
|
37
38
|
return
|
|
38
39
|
if control:
|
|
39
|
-
|
|
40
|
+
# 当前控件信息
|
|
40
41
|
global current_control
|
|
41
42
|
current_control = control
|
|
42
|
-
|
|
43
|
+
# 绘制矩形
|
|
43
44
|
canvas_info = win_canvas.CanvasInfo(control.Name, [
|
|
44
45
|
(control.BoundingRectangle.left, control.BoundingRectangle.top),
|
|
45
46
|
(control.BoundingRectangle.right, control.BoundingRectangle.top),
|
|
@@ -65,6 +66,7 @@ def get_control_from_cursor():
|
|
|
65
66
|
# time.sleep(2)
|
|
66
67
|
# xx.Click()
|
|
67
68
|
|
|
69
|
+
|
|
68
70
|
def on_press(key):
|
|
69
71
|
global ctrl_pressed, keyboard_listener
|
|
70
72
|
if key == keyboard.Key.ctrl_l and not ctrl_pressed:
|
|
@@ -78,6 +80,7 @@ def on_press(key):
|
|
|
78
80
|
keyboard_listener.stop()
|
|
79
81
|
# pg.alert('采集成功!')
|
|
80
82
|
|
|
83
|
+
|
|
81
84
|
def on_release(key):
|
|
82
85
|
global ctrl_pressed
|
|
83
86
|
if key == keyboard.Key.ctrl_l:
|
|
@@ -104,6 +107,7 @@ def get_control_json():
|
|
|
104
107
|
time.sleep(0.5)
|
|
105
108
|
return control_json, screenshot_path
|
|
106
109
|
|
|
110
|
+
|
|
107
111
|
if __name__ == '__main__':
|
|
108
112
|
a = get_control_json()
|
|
109
113
|
print(a)
|
ctools/call.py
CHANGED
|
@@ -1,71 +1,56 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import sched
|
|
2
3
|
import threading
|
|
3
4
|
import time
|
|
4
5
|
from functools import wraps
|
|
5
6
|
|
|
7
|
+
|
|
6
8
|
# annotation
|
|
9
|
+
_global_once_cache = {}
|
|
7
10
|
def once(func):
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
:param func: function to be initialized
|
|
11
|
-
:return: the real decorator for return the result
|
|
12
|
-
"""
|
|
13
|
-
initialized = False
|
|
14
|
-
res = None
|
|
15
|
-
|
|
11
|
+
code = func.__code__
|
|
12
|
+
key = f"{os.path.abspath(code.co_filename)}:{code.co_firstlineno}"
|
|
16
13
|
def wrapper(*args, **kwargs):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
initialized = True
|
|
21
|
-
return res
|
|
22
|
-
else:
|
|
23
|
-
return res
|
|
24
|
-
|
|
14
|
+
if key not in _global_once_cache:
|
|
15
|
+
_global_once_cache[key] = func(*args, **kwargs)
|
|
16
|
+
return _global_once_cache[key]
|
|
25
17
|
return wrapper
|
|
26
18
|
|
|
27
19
|
# annotation
|
|
20
|
+
_cache = {}
|
|
28
21
|
def init(func):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
res = func()
|
|
35
|
-
|
|
22
|
+
code = func.__code__
|
|
23
|
+
key = f"{os.path.abspath(code.co_filename)}:{code.co_firstlineno}"
|
|
24
|
+
if key not in _cache:
|
|
25
|
+
_cache[key] = func()
|
|
36
26
|
def wrapper():
|
|
37
|
-
return
|
|
38
|
-
|
|
27
|
+
return _cache[key]
|
|
39
28
|
return wrapper
|
|
40
29
|
|
|
41
30
|
# annotation
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
run_flag = False
|
|
45
|
-
scheduler = sched.scheduler(time.time, time.sleep)
|
|
46
|
-
|
|
31
|
+
_scheduler_cache = {}
|
|
32
|
+
def schd(interval_seconds, start_by_call=False, run_now=False):
|
|
47
33
|
def decorator(func):
|
|
34
|
+
key = f"{os.path.abspath(func.__code__.co_filename)}:{func.__code__.co_firstlineno}"
|
|
35
|
+
lock = threading.Lock()
|
|
48
36
|
@wraps(func)
|
|
49
37
|
def wrapper(*args, **kwargs):
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
threading.Thread(target=wrapper, args=args, kwargs=kwargs, daemon=True).start()
|
|
54
|
-
return
|
|
55
|
-
|
|
38
|
+
if key in _scheduler_cache:
|
|
39
|
+
return # 已经调度过
|
|
40
|
+
scheduler = sched.scheduler(time.time, time.sleep)
|
|
56
41
|
def job():
|
|
57
42
|
func(*args, **kwargs)
|
|
58
43
|
scheduler.enter(interval_seconds, 1, job)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if not start_by_call:
|
|
44
|
+
def start_scheduler():
|
|
45
|
+
with lock:
|
|
46
|
+
if _scheduler_cache.get(key): return
|
|
47
|
+
_scheduler_cache[key] = True
|
|
48
|
+
if run_now:
|
|
49
|
+
func(*args, **kwargs)
|
|
50
|
+
scheduler.enter(interval_seconds, 1, job)
|
|
51
|
+
scheduler.run()
|
|
52
|
+
threading.Thread(target=start_scheduler, daemon=True).start()
|
|
53
|
+
if not start_by_call:
|
|
54
|
+
wrapper()
|
|
69
55
|
return wrapper
|
|
70
|
-
|
|
71
56
|
return decorator
|
ctools/cdate.py
CHANGED
|
@@ -1,39 +1,52 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from datetime import datetime, timedelta
|
|
3
3
|
|
|
4
|
+
def get_month(date: str=None):
|
|
5
|
+
if date: return time.strftime('%Y-%m', time.strptime(date, '%Y-%m-%d'))
|
|
6
|
+
return time.strftime('%Y-%m', time.localtime(time.time()))
|
|
7
|
+
|
|
4
8
|
def get_date():
|
|
5
9
|
return time.strftime('%Y-%m-%d', time.localtime(time.time()))
|
|
6
10
|
|
|
11
|
+
|
|
7
12
|
def get_time():
|
|
8
13
|
return time.strftime('%H-%M-%S', time.localtime(time.time()))
|
|
9
14
|
|
|
15
|
+
|
|
10
16
|
def get_date_time(fmt="%Y-%m-%d %H:%M:%S"):
|
|
11
17
|
return time.strftime(fmt, time.localtime(time.time()))
|
|
12
18
|
|
|
19
|
+
|
|
13
20
|
def str_to_datetime(val: str, fmt="%Y-%m-%d %H:%M:%S"):
|
|
14
21
|
return time.strptime(val, fmt)
|
|
15
22
|
|
|
23
|
+
|
|
16
24
|
def str_to_timestamp(val: str, fmt="%Y-%m-%d %H:%M:%S"):
|
|
17
25
|
return time.mktime(time.strptime(val, fmt))
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
|
|
28
|
+
def timestamp_to_str(timestamp: int = time.time(), fmt="%Y-%m-%d %H:%M:%S"):
|
|
20
29
|
return time.strftime(fmt, time.localtime(timestamp))
|
|
21
30
|
|
|
31
|
+
|
|
22
32
|
def get_today_start_end(now: datetime.now()):
|
|
23
33
|
start = datetime(now.year, now.month, now.day, 0, 0, 0)
|
|
24
34
|
end = datetime(now.year, now.month, now.day, 23, 59, 59)
|
|
25
35
|
return start.strftime("%Y-%m-%d %H:%M:%S"), end.strftime("%Y-%m-%d %H:%M:%S")
|
|
26
36
|
|
|
37
|
+
|
|
27
38
|
def get_week_start_end(now: datetime.now()):
|
|
28
39
|
start = now - timedelta(days=now.weekday()) # 本周一
|
|
29
40
|
end = start + timedelta(days=6) # 本周日
|
|
30
41
|
return start.strftime("%Y-%m-%d 00:00:00"), end.strftime("%Y-%m-%d 23:59:59")
|
|
31
42
|
|
|
32
|
-
|
|
43
|
+
|
|
44
|
+
def time_diff_in_seconds(sub_head: str = get_date_time(), sub_end: str = get_date_time()):
|
|
33
45
|
start_ts = str_to_timestamp(sub_head)
|
|
34
46
|
end_ts = str_to_timestamp(sub_end)
|
|
35
47
|
return int(start_ts - end_ts)
|
|
36
48
|
|
|
49
|
+
|
|
37
50
|
def opt_time(base_time=None, days=0, hours=0, minutes=0, seconds=0, weeks=0, fmt="%Y-%m-%d %H:%M:%S"):
|
|
38
51
|
if base_time is None:
|
|
39
52
|
base_time = datetime.now()
|
|
@@ -41,3 +54,31 @@ def opt_time(base_time=None, days=0, hours=0, minutes=0, seconds=0, weeks=0, fmt
|
|
|
41
54
|
base_time = datetime.strptime(base_time, fmt)
|
|
42
55
|
new_time = base_time + timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds, weeks=weeks)
|
|
43
56
|
return new_time.strftime(fmt)
|
|
57
|
+
|
|
58
|
+
def get_years_range(start_year: str, end_year: str=datetime.now().strftime("%Y")) -> set[int]:
|
|
59
|
+
if int(start_year) > int(end_year):
|
|
60
|
+
raise ValueError("起始年份不能大于结束年份")
|
|
61
|
+
return list(range(int(start_year), int(end_year) + 1))
|
|
62
|
+
|
|
63
|
+
def get_month_range(start: str, end: str=datetime.now().strftime("%Y-%m")) -> set[str]:
|
|
64
|
+
start_date = datetime.strptime(start, "%Y-%m")
|
|
65
|
+
end_date = datetime.strptime(end, "%Y-%m")
|
|
66
|
+
if start_date > end_date:
|
|
67
|
+
raise ValueError("起始月份不能晚于结束月份")
|
|
68
|
+
result = []
|
|
69
|
+
current = start_date
|
|
70
|
+
while current <= end_date:
|
|
71
|
+
result.append(current.strftime("%Y-%m"))
|
|
72
|
+
if current.month == 12:
|
|
73
|
+
current = current.replace(year=current.year + 1, month=1)
|
|
74
|
+
else:
|
|
75
|
+
current = current.replace(month=current.month + 1)
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def get_day_range(start: str, end: str=datetime.now().strftime("%Y-%m-%d")) -> set[str]:
|
|
79
|
+
start_date = datetime.strptime(start, "%Y-%m-%d")
|
|
80
|
+
end_date = datetime.strptime(end, "%Y-%m-%d")
|
|
81
|
+
if start_date > end_date:
|
|
82
|
+
raise ValueError("起始日期不能晚于结束日期")
|
|
83
|
+
delta = end_date - start_date
|
|
84
|
+
return [(start_date + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(delta.days + 1)]
|
ctools/cid.py
CHANGED
|
@@ -2,17 +2,19 @@ from ctools.util.snow_id import SnowId
|
|
|
2
2
|
|
|
3
3
|
idWorker = SnowId(1, 2, 0)
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def get_snowflake_id():
|
|
6
7
|
return idWorker.get_id()
|
|
7
8
|
|
|
9
|
+
|
|
10
|
+
def get_snowflake_id_str():
|
|
11
|
+
return str(get_snowflake_id())
|
|
12
|
+
|
|
8
13
|
def get_random_str(size: int = 10) -> str:
|
|
9
14
|
import random
|
|
10
15
|
return "".join(random.sample('abcdefghjklmnpqrstuvwxyz123456789', size))
|
|
11
16
|
|
|
17
|
+
|
|
12
18
|
def get_uuid() -> str:
|
|
13
19
|
import uuid
|
|
14
20
|
return str(uuid.uuid1()).replace("-", "")
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
ctools/cipher/aes_util.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from cryptography.fernet import Fernet
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
def generate_key():
|
|
4
5
|
"""
|
|
5
6
|
生成 AES key
|
|
@@ -9,6 +10,7 @@ def generate_key():
|
|
|
9
10
|
key = Fernet.generate_key()
|
|
10
11
|
return key.decode()
|
|
11
12
|
|
|
13
|
+
|
|
12
14
|
def aes_encrypt(sec_key, plaintext):
|
|
13
15
|
"""
|
|
14
16
|
aes加密
|
|
@@ -31,5 +33,3 @@ def aes_decrypt(sec_key, ciphertext):
|
|
|
31
33
|
cipher = Fernet(sec_key)
|
|
32
34
|
decrypted_data = cipher.decrypt(ciphertext)
|
|
33
35
|
return decrypted_data.decode()
|
|
34
|
-
|
|
35
|
-
|
ctools/cipher/b64.py
CHANGED
ctools/cipher/czip.py
CHANGED
|
@@ -22,6 +22,8 @@ import pyzipper
|
|
|
22
22
|
output_directory = '/Users/haoyang/Desktop'
|
|
23
23
|
compress_specific_files(files_to_compress, output_directory, zip_password, "my_files")
|
|
24
24
|
"""
|
|
25
|
+
|
|
26
|
+
|
|
25
27
|
def create_zip_with_files(file_dict, password=None) -> io.BytesIO:
|
|
26
28
|
"""Compress multiple files into a single password-protected ZIP archive in memory.
|
|
27
29
|
Args:
|
|
@@ -87,7 +89,7 @@ def process_directory_to_single_zip(root_dir, password=None, zip_name=None):
|
|
|
87
89
|
if 'zip_buffer' in locals(): zip_buffer.close()
|
|
88
90
|
|
|
89
91
|
|
|
90
|
-
def compress_specific_files(file_paths:[], output_dir:str, password=None, zip_name=None):
|
|
92
|
+
def compress_specific_files(file_paths: [], output_dir: str, password=None, zip_name=None):
|
|
91
93
|
"""Compress multiple specified files into a single ZIP archive.
|
|
92
94
|
Args:
|
|
93
95
|
file_paths: List of absolute file paths to compress
|
ctools/cipher/rsa.py
CHANGED
|
@@ -10,6 +10,7 @@ from ctools import cjson, path_info
|
|
|
10
10
|
ENCRYPT_CHUNK_SIZE = 245
|
|
11
11
|
decrypt_CHUNK_SIZE = 512
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
def generate_rsa_keypair(bits=2048):
|
|
14
15
|
key = RSA.generate(bits)
|
|
15
16
|
private_key = key.export_key() # 导出私钥
|
|
@@ -19,11 +20,13 @@ def generate_rsa_keypair(bits=2048):
|
|
|
19
20
|
with open("public_key.pem", "wb") as f:
|
|
20
21
|
f.write(public_key)
|
|
21
22
|
|
|
23
|
+
|
|
22
24
|
def loadLicenseInfo(auth_code):
|
|
23
25
|
with open(path_info.get_app_path() + '/keys/license.key', 'r') as pri:
|
|
24
26
|
decrypt_message = decrypt(auth_code.strip(), pri.read())
|
|
25
27
|
return cjson.loads(decrypt_message)
|
|
26
28
|
|
|
29
|
+
|
|
27
30
|
# 加密函数
|
|
28
31
|
def encrypt(msg, public_key):
|
|
29
32
|
parts = b''
|
|
@@ -34,6 +37,7 @@ def encrypt(msg, public_key):
|
|
|
34
37
|
encrypted_base64 = base64.b64encode(parts)
|
|
35
38
|
return encrypted_base64.decode()
|
|
36
39
|
|
|
40
|
+
|
|
37
41
|
# 解密函数
|
|
38
42
|
def decrypt(msg, private_key):
|
|
39
43
|
parts = b''
|
|
@@ -44,6 +48,7 @@ def decrypt(msg, private_key):
|
|
|
44
48
|
parts += cipher.decrypt(encrypted_bytes[i:i + decrypt_CHUNK_SIZE])
|
|
45
49
|
return parts.decode()
|
|
46
50
|
|
|
51
|
+
|
|
47
52
|
# 验签
|
|
48
53
|
def verify_sign(msg, public_key, sign):
|
|
49
54
|
public_key = RSA.import_key(public_key)
|
|
@@ -55,6 +60,7 @@ def verify_sign(msg, public_key, sign):
|
|
|
55
60
|
print('签名验证失败: {}'.format(e))
|
|
56
61
|
return False
|
|
57
62
|
|
|
63
|
+
|
|
58
64
|
# 签名
|
|
59
65
|
def sign_msg(msg, private_key):
|
|
60
66
|
private_key = RSA.import_key(private_key)
|
|
@@ -62,7 +68,6 @@ def sign_msg(msg, private_key):
|
|
|
62
68
|
signature = pkcs1_15.new(private_key).sign(hash_message)
|
|
63
69
|
return base64.b64encode(signature).decode()
|
|
64
70
|
|
|
65
|
-
|
|
66
71
|
# with open(work_path.get_current_path() + '/private_key.pem', 'r') as key:
|
|
67
72
|
# key = key.read()
|
|
68
73
|
# sign = sign_msg(key, key)
|
ctools/cipher/sign.py
CHANGED
|
@@ -14,6 +14,7 @@ def generate_signature(file_path, key: str = global_key):
|
|
|
14
14
|
except:
|
|
15
15
|
return ''
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
def digest(value: str, key: str = global_key):
|
|
18
19
|
val_hash = hashlib.sha256(value.encode()).digest()
|
|
19
20
|
sign_val = hmac.new(key.encode(), val_hash, hashlib.sha256).digest()
|
ctools/cipher/sm_util.py
CHANGED
|
@@ -5,6 +5,7 @@ from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
|
|
|
5
5
|
|
|
6
6
|
sm2_crypt: sm2.CryptSM2 = None
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
def init(private_key: str, public_key: str):
|
|
9
10
|
global sm2_crypt
|
|
10
11
|
if sm2_crypt is not None:
|
|
@@ -12,6 +13,7 @@ def init(private_key: str, public_key: str):
|
|
|
12
13
|
return
|
|
13
14
|
sm2_crypt = sm2.CryptSM2(private_key=private_key, public_key=public_key, asn1=True, mode=1)
|
|
14
15
|
|
|
16
|
+
|
|
15
17
|
def sign_with_sm2(sign_data: str) -> str:
|
|
16
18
|
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
|
17
19
|
return sm2_crypt.sign_with_sm3(sign_data.encode('UTF-8'))
|
|
@@ -25,6 +27,7 @@ def verify_with_sm2(sign_val: str, sign_data: str) -> bool:
|
|
|
25
27
|
print('签名验证失败: {}'.format(e))
|
|
26
28
|
return False
|
|
27
29
|
|
|
30
|
+
|
|
28
31
|
def encrypt_with_sm2(encrypt_data: str) -> str:
|
|
29
32
|
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
|
30
33
|
return base64.b64encode(sm2_crypt.encrypt(encrypt_data.encode('UTF-8'))).decode('UTF-8')
|