gomyck-tools 1.4.0__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.
- ctools/__init__.py +21 -0
- ctools/ai/env_config.py +20 -1
- ctools/ai/llm_chat.py +8 -8
- ctools/ai/llm_client.py +37 -28
- ctools/ai/mcp/mcp_client.py +33 -16
- ctools/ai/tools/json_extract.py +3 -2
- ctools/ai/tools/quick_tools.py +68 -21
- ctools/ai/tools/tool_use_xml_parse.py +2 -1
- ctools/ai/tools/xml_extract.py +3 -0
- ctools/application.py +19 -17
- 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 +18 -22
- ctools/cdate.py +43 -2
- ctools/cid.py +3 -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 +37 -17
- ctools/dict_wrapper.py +1 -0
- ctools/ex.py +1 -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/path_info.py +29 -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 +29 -21
- ctools/stream/mqtt_utils.py +2 -2
- ctools/sys_info.py +8 -0
- ctools/sys_log.py +3 -0
- ctools/util/cftp.py +4 -2
- ctools/util/http_util.py +1 -0
- ctools/util/image_process.py +1 -1
- 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 +15 -2
- ctools/web/bottle_webserver.py +14 -8
- ctools/web/bottle_websocket.py +4 -0
- ctools/web/ctoken.py +5 -1
- 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.0.dist-info → gomyck_tools-1.4.2.dist-info}/METADATA +2 -1
- gomyck_tools-1.4.2.dist-info/RECORD +82 -0
- gomyck_tools-1.4.0.dist-info/RECORD +0 -82
- {gomyck_tools-1.4.0.dist-info → gomyck_tools-1.4.2.dist-info}/WHEEL +0 -0
- {gomyck_tools-1.4.0.dist-info → gomyck_tools-1.4.2.dist-info}/licenses/LICENSE +0 -0
- {gomyck_tools-1.4.0.dist-info → gomyck_tools-1.4.2.dist-info}/top_level.txt +0 -0
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
@@ -3,6 +3,7 @@ import threading
|
|
3
3
|
import time
|
4
4
|
from functools import wraps
|
5
5
|
|
6
|
+
|
6
7
|
# annotation
|
7
8
|
def once(func):
|
8
9
|
"""
|
@@ -24,6 +25,7 @@ def once(func):
|
|
24
25
|
|
25
26
|
return wrapper
|
26
27
|
|
28
|
+
|
27
29
|
# annotation
|
28
30
|
def init(func):
|
29
31
|
"""
|
@@ -38,34 +40,28 @@ def init(func):
|
|
38
40
|
|
39
41
|
return wrapper
|
40
42
|
|
43
|
+
|
41
44
|
# annotation
|
42
|
-
def schd(interval_seconds, start_by_call
|
43
|
-
start_flag = False
|
44
|
-
run_flag = False
|
45
|
+
def schd(interval_seconds, start_by_call=False, run_now=False):
|
45
46
|
scheduler = sched.scheduler(time.time, time.sleep)
|
46
|
-
|
47
|
+
lock = threading.Lock()
|
48
|
+
started = [False] # 可变对象,线程可见
|
49
|
+
print("schd delay is: ", interval_seconds)
|
47
50
|
def decorator(func):
|
48
51
|
@wraps(func)
|
49
52
|
def wrapper(*args, **kwargs):
|
50
|
-
nonlocal start_flag
|
51
|
-
if start_by_call and not start_flag:
|
52
|
-
start_flag = True
|
53
|
-
threading.Thread(target=wrapper, args=args, kwargs=kwargs, daemon=True).start()
|
54
|
-
return
|
55
|
-
|
56
53
|
def job():
|
57
54
|
func(*args, **kwargs)
|
58
55
|
scheduler.enter(interval_seconds, 1, job)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
if not start_by_call:
|
69
|
-
return wrapper
|
70
|
-
|
56
|
+
def start_scheduler():
|
57
|
+
with lock:
|
58
|
+
if started[0]: return
|
59
|
+
started[0] = True
|
60
|
+
if run_now: func(*args, **kwargs)
|
61
|
+
scheduler.enter(interval_seconds, 1, job)
|
62
|
+
scheduler.run()
|
63
|
+
threading.Thread(target=start_scheduler, daemon=True).start()
|
64
|
+
# 如果不是手动触发,则自动启动一次(无参数)
|
65
|
+
if not start_by_call: wrapper()
|
66
|
+
return wrapper # 如果是 start_by_call=True,返回 wrapper 让用户手动调用时带参
|
71
67
|
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,16 @@ 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
|
+
|
8
10
|
def get_random_str(size: int = 10) -> str:
|
9
11
|
import random
|
10
12
|
return "".join(random.sample('abcdefghjklmnpqrstuvwxyz123456789', size))
|
11
13
|
|
14
|
+
|
12
15
|
def get_uuid() -> str:
|
13
16
|
import uuid
|
14
17
|
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')
|
ctools/cjson.py
CHANGED
@@ -6,6 +6,7 @@ jsonpickle.set_preferred_backend('json')
|
|
6
6
|
jsonpickle.set_encoder_options('json', ensure_ascii=False)
|
7
7
|
jsonpickle.set_decoder_options('json')
|
8
8
|
|
9
|
+
|
9
10
|
def dumps(obj, **kwargs) -> str:
|
10
11
|
"""
|
11
12
|
将对象转换为json字符串
|
@@ -17,6 +18,7 @@ def dumps(obj, **kwargs) -> str:
|
|
17
18
|
if type(obj) == str: return obj
|
18
19
|
return f'{jsonpickle.encode(obj, unpicklable=False, make_refs=False, **kwargs)}'
|
19
20
|
|
21
|
+
|
20
22
|
def loads(json_str: str, **kwargs) -> dict:
|
21
23
|
"""
|
22
24
|
将json字符串转换为对象
|
@@ -25,6 +27,7 @@ def loads(json_str: str, **kwargs) -> dict:
|
|
25
27
|
"""
|
26
28
|
return jsonpickle.decode(json_str, **kwargs)
|
27
29
|
|
30
|
+
|
28
31
|
def unify_to_str(json_str: str) -> str:
|
29
32
|
if not str_value_keys and len(str_value_keys) == 0: return json_str
|
30
33
|
obj = loads(json_str)
|
@@ -34,6 +37,7 @@ def unify_to_str(json_str: str) -> str:
|
|
34
37
|
_handle_dict(obj)
|
35
38
|
return dumps(obj)
|
36
39
|
|
40
|
+
|
37
41
|
def _handle_list(data):
|
38
42
|
for o in data:
|
39
43
|
if isinstance(o, list):
|
@@ -41,6 +45,7 @@ def _handle_list(data):
|
|
41
45
|
elif isinstance(o, dict):
|
42
46
|
_handle_dict(o)
|
43
47
|
|
48
|
+
|
44
49
|
def _handle_dict(data):
|
45
50
|
for k, v in data.items():
|
46
51
|
if isinstance(v, list):
|
ctools/cron_lite.py
CHANGED
@@ -29,6 +29,7 @@ print(123123)
|
|
29
29
|
cron_lite.start_all()
|
30
30
|
"""
|
31
31
|
|
32
|
+
|
32
33
|
class SchedulerMeta:
|
33
34
|
timer_task_name: str = None
|
34
35
|
switch: bool = True
|
@@ -40,7 +41,7 @@ class SchedulerMeta:
|
|
40
41
|
|
41
42
|
scheduler_map: Dict[str, SchedulerMeta] = {} # {timer_task_name: SchedulerMeta}
|
42
43
|
_switch = False
|
43
|
-
_info_handler
|
44
|
+
_info_handler = print
|
44
45
|
_error_handler = print
|
45
46
|
_time_zone: Optional[pytz.BaseTzInfo] = pytz.timezone("Asia/Shanghai")
|
46
47
|
|
@@ -49,6 +50,7 @@ def set_time_zone(time_zone_name: str):
|
|
49
50
|
global _time_zone
|
50
51
|
_time_zone = pytz.timezone(time_zone_name)
|
51
52
|
|
53
|
+
|
52
54
|
# @annotation
|
53
55
|
def cron_task(cron_expr: str, task_name: str = None, till_time_stamp: int = None):
|
54
56
|
"""
|
@@ -92,8 +94,9 @@ def reg_cron_task(cron_expr, func, params, timer_task_name=None, till_time_stamp
|
|
92
94
|
:return: the real decorator
|
93
95
|
"""
|
94
96
|
cron_expr = _convert_cron(cron_expr)
|
95
|
-
assert len(cron_expr.split(" ")) in (5, 6),
|
97
|
+
assert len(cron_expr.split(" ")) in (5, 6), "Only supported <minute hour day month weekday> and <minute hour day month weekday second>"
|
96
98
|
task_name = func.__name__ if timer_task_name is None else timer_task_name
|
99
|
+
|
97
100
|
@wraps(func)
|
98
101
|
def wrapper(*args, **kwargs):
|
99
102
|
try:
|
@@ -107,6 +110,7 @@ def reg_cron_task(cron_expr, func, params, timer_task_name=None, till_time_stamp
|
|
107
110
|
|
108
111
|
_register_next(task_name, wrapper, cron_expr, till_time_stamp, init=True)
|
109
112
|
|
113
|
+
|
110
114
|
def start_all(spawn: bool = True, daemon: bool = True, info_handler=None, error_handler=None) -> Optional[threading.Thread]:
|
111
115
|
"""
|
112
116
|
start_all starts all cron tasks registered before.
|
@@ -144,6 +148,7 @@ def active(timer_task_name):
|
|
144
148
|
if timer_task_name in scheduler_map:
|
145
149
|
scheduler_map.get(timer_task_name).status = True
|
146
150
|
|
151
|
+
|
147
152
|
def get_switch(timer_task_name):
|
148
153
|
switch = True
|
149
154
|
if timer_task_name in scheduler_map:
|
@@ -215,6 +220,7 @@ def _run_sched(scheduler_meta: SchedulerMeta):
|
|
215
220
|
return
|
216
221
|
time.sleep(0.5)
|
217
222
|
|
223
|
+
|
218
224
|
def _start(taskName: str = None):
|
219
225
|
global _switch
|
220
226
|
_switch = True
|
@@ -223,7 +229,7 @@ def _start(taskName: str = None):
|
|
223
229
|
for timer_task_name, scheduler_meta in scheduler_map.items():
|
224
230
|
if taskName is not None and timer_task_name != taskName: continue
|
225
231
|
print("register job: ", timer_task_name, ", cron: ", scheduler_meta.cron_str)
|
226
|
-
thread = threading.Thread(target=_run_sched, args=(scheduler_meta,
|
232
|
+
thread = threading.Thread(target=_run_sched, args=(scheduler_meta,), daemon=True)
|
227
233
|
thread.start()
|
228
234
|
taskList.append(thread)
|
229
235
|
for task in taskList: task.join()
|
@@ -231,6 +237,7 @@ def _start(taskName: str = None):
|
|
231
237
|
_switch = False
|
232
238
|
scheduler_map.clear()
|
233
239
|
|
240
|
+
|
234
241
|
def _convert_cron(cron_expr):
|
235
242
|
res_cron = ""
|
236
243
|
cron_list = cron_expr.split(" ")
|
@@ -242,4 +249,3 @@ def _convert_cron(cron_expr):
|
|
242
249
|
else:
|
243
250
|
res_cron = cron_expr
|
244
251
|
return res_cron
|
245
|
-
|