gomyck-tools 1.0.0__py3-none-any.whl → 1.4.7__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/__init__.py +4 -0
- ctools/ai/llm_chat.py +184 -0
- ctools/ai/llm_client.py +163 -0
- ctools/ai/llm_exception.py +17 -0
- ctools/ai/mcp/__init__.py +4 -0
- ctools/ai/mcp/mcp_client.py +326 -0
- ctools/ai/tools/__init__.py +4 -0
- ctools/ai/tools/json_extract.py +202 -0
- ctools/ai/tools/quick_tools.py +149 -0
- ctools/ai/tools/think_process.py +11 -0
- ctools/ai/tools/tool_use_xml_parse.py +40 -0
- ctools/ai/tools/xml_extract.py +15 -0
- ctools/application.py +50 -47
- ctools/aspect.py +65 -0
- ctools/auto/__init__.py +4 -0
- ctools/{browser_element_tools.py → auto/browser_element.py} +18 -8
- ctools/{plan_area_tools.py → auto/plan_area.py} +5 -7
- ctools/{pty_tools.py → auto/pty_process.py} +6 -3
- ctools/{resource_bundle_tools.py → auto/resource_bundle.py} +4 -4
- ctools/{screenshot_tools.py → auto/screenshot.py} +7 -6
- ctools/{win_canvas.py → auto/win_canvas.py} +10 -4
- ctools/{win_control.py → auto/win_control.py} +14 -5
- ctools/call.py +34 -49
- ctools/cdate.py +84 -0
- ctools/cdebug.py +146 -0
- ctools/cid.py +20 -0
- ctools/cipher/__init__.py +4 -0
- ctools/{aes_tools.py → cipher/aes_util.py} +10 -0
- ctools/{b64.py → cipher/b64.py} +2 -0
- ctools/cipher/czip.py +133 -0
- ctools/cipher/rsa.py +75 -0
- ctools/{sign.py → cipher/sign.py} +2 -1
- ctools/{sm_tools.py → cipher/sm_util.py} +20 -4
- ctools/cjson.py +10 -10
- ctools/cron_lite.py +109 -97
- ctools/database/__init__.py +4 -0
- ctools/{database.py → database/database.py} +93 -26
- ctools/dict_wrapper.py +21 -0
- ctools/ex.py +4 -0
- ctools/geo/__init__.py +4 -0
- ctools/geo/coord_trans.py +127 -0
- ctools/geo/douglas_rarefy.py +139 -0
- ctools/metrics.py +56 -63
- ctools/office/__init__.py +4 -0
- ctools/office/cword.py +30 -0
- ctools/{word_fill.py → office/word_fill.py} +3 -6
- ctools/patch.py +88 -0
- ctools/{work_path.py → path_info.py} +34 -2
- ctools/pkg/__init__.py +4 -0
- ctools/pkg/dynamic_imp.py +38 -0
- ctools/pools/__init__.py +4 -0
- ctools/pools/process_pool.py +41 -0
- ctools/{thread_pool.py → pools/thread_pool.py} +13 -4
- ctools/similar.py +25 -0
- ctools/stream/__init__.py +4 -0
- ctools/stream/ckafka.py +164 -0
- ctools/stream/credis.py +127 -0
- ctools/{mqtt_utils.py → stream/mqtt_utils.py} +20 -12
- ctools/sys_info.py +36 -13
- ctools/sys_log.py +46 -27
- ctools/util/__init__.py +4 -0
- ctools/util/cftp.py +76 -0
- ctools/util/cklock.py +118 -0
- ctools/util/config_util.py +52 -0
- ctools/util/env_config.py +63 -0
- ctools/{html_soup.py → util/html_soup.py} +0 -7
- ctools/{http_utils.py → util/http_util.py} +4 -2
- ctools/{images_tools.py → util/image_process.py} +10 -1
- ctools/util/jb_cut.py +54 -0
- ctools/{id_worker_tools.py → util/snow_id.py} +8 -23
- ctools/web/__init__.py +4 -0
- ctools/web/aio_web_server.py +186 -0
- ctools/web/api_result.py +56 -0
- ctools/web/bottle_web_base.py +239 -0
- ctools/web/bottle_webserver.py +191 -0
- ctools/web/bottle_websocket.py +79 -0
- ctools/web/ctoken.py +103 -0
- ctools/{download_tools.py → web/download_util.py} +14 -12
- ctools/web/params_util.py +46 -0
- ctools/{upload_tools.py → web/upload_util.py} +3 -2
- gomyck_tools-1.4.7.dist-info/METADATA +70 -0
- gomyck_tools-1.4.7.dist-info/RECORD +88 -0
- {gomyck_tools-1.0.0.dist-info → gomyck_tools-1.4.7.dist-info}/WHEEL +1 -1
- gomyck_tools-1.4.7.dist-info/licenses/LICENSE +13 -0
- ctools/bashPath.py +0 -13
- ctools/bottle_server.py +0 -49
- ctools/console.py +0 -55
- ctools/date_utils.py +0 -44
- ctools/enums.py +0 -4
- ctools/excelOpt.py +0 -36
- ctools/imgDialog.py +0 -44
- ctools/license.py +0 -37
- ctools/log.py +0 -28
- ctools/mvc.py +0 -56
- ctools/obj.py +0 -20
- ctools/pacth.py +0 -73
- ctools/ssh.py +0 -9
- ctools/strDiff.py +0 -20
- ctools/string_tools.py +0 -101
- ctools/token_tools.py +0 -13
- ctools/wordFill.py +0 -24
- gomyck_tools-1.0.0.dist-info/METADATA +0 -20
- gomyck_tools-1.0.0.dist-info/RECORD +0 -54
- /ctools/{word_fill_entity.py → office/word_fill_entity.py} +0 -0
- /ctools/{compile_tools.py → util/compile_util.py} +0 -0
- {gomyck_tools-1.0.0.dist-info → gomyck_tools-1.4.7.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import time
|
|
3
|
+
|
|
3
4
|
import pyautogui
|
|
4
5
|
import uiautomation as auto
|
|
5
6
|
from pynput import keyboard
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
from ctools import application
|
|
9
|
+
from ctools import cid
|
|
10
|
+
from ctools.auto import win_canvas
|
|
11
|
+
from ctools.pools import thread_pool
|
|
7
12
|
|
|
8
13
|
current_control = None
|
|
9
14
|
ctrl_pressed = False
|
|
@@ -11,13 +16,14 @@ keyboard_listener = None
|
|
|
11
16
|
control_json = {}
|
|
12
17
|
screenshot_path = ""
|
|
13
18
|
|
|
19
|
+
|
|
14
20
|
class ControlInfo:
|
|
15
21
|
def __init__(self, ControlType, ClassName, AutomationId, Name, Depth):
|
|
16
22
|
self.ControlType = ControlType
|
|
17
23
|
self.ClassName = ClassName
|
|
18
24
|
self.AutomationId = AutomationId
|
|
19
25
|
self.Name = Name
|
|
20
|
-
#self.Depth = Depth
|
|
26
|
+
# self.Depth = Depth
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
def get_control_from_cursor():
|
|
@@ -31,10 +37,10 @@ def get_control_from_cursor():
|
|
|
31
37
|
print("No control found {}".format(e))
|
|
32
38
|
return
|
|
33
39
|
if control:
|
|
34
|
-
|
|
40
|
+
# 当前控件信息
|
|
35
41
|
global current_control
|
|
36
42
|
current_control = control
|
|
37
|
-
|
|
43
|
+
# 绘制矩形
|
|
38
44
|
canvas_info = win_canvas.CanvasInfo(control.Name, [
|
|
39
45
|
(control.BoundingRectangle.left, control.BoundingRectangle.top),
|
|
40
46
|
(control.BoundingRectangle.right, control.BoundingRectangle.top),
|
|
@@ -53,13 +59,14 @@ def get_control_from_cursor():
|
|
|
53
59
|
|
|
54
60
|
img = pyautogui.screenshot(region=[control.BoundingRectangle.left, control.BoundingRectangle.top,
|
|
55
61
|
control.BoundingRectangle.width(), control.BoundingRectangle.height()])
|
|
56
|
-
screenshot_path = os.path.join(application.Server.screenshotPath, "screenshot-%s.png" %
|
|
62
|
+
screenshot_path = os.path.join(application.Server.screenshotPath, "screenshot-%s.png" % cid.get_snowflake_id())
|
|
57
63
|
img.save(screenshot_path)
|
|
58
64
|
# xx = auto.Control(**control_json)
|
|
59
65
|
# print(control_json)
|
|
60
66
|
# time.sleep(2)
|
|
61
67
|
# xx.Click()
|
|
62
68
|
|
|
69
|
+
|
|
63
70
|
def on_press(key):
|
|
64
71
|
global ctrl_pressed, keyboard_listener
|
|
65
72
|
if key == keyboard.Key.ctrl_l and not ctrl_pressed:
|
|
@@ -73,6 +80,7 @@ def on_press(key):
|
|
|
73
80
|
keyboard_listener.stop()
|
|
74
81
|
# pg.alert('采集成功!')
|
|
75
82
|
|
|
83
|
+
|
|
76
84
|
def on_release(key):
|
|
77
85
|
global ctrl_pressed
|
|
78
86
|
if key == keyboard.Key.ctrl_l:
|
|
@@ -99,6 +107,7 @@ def get_control_json():
|
|
|
99
107
|
time.sleep(0.5)
|
|
100
108
|
return control_json, screenshot_path
|
|
101
109
|
|
|
110
|
+
|
|
102
111
|
if __name__ == '__main__':
|
|
103
112
|
a = get_control_json()
|
|
104
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
|
|
|
6
7
|
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
run_flag = False
|
|
45
|
-
scheduler = sched.scheduler(time.time, time.sleep)
|
|
46
|
-
|
|
30
|
+
# annotation
|
|
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
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from datetime import datetime, timedelta
|
|
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
|
+
|
|
8
|
+
def get_date():
|
|
9
|
+
return time.strftime('%Y-%m-%d', time.localtime(time.time()))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_time():
|
|
13
|
+
return time.strftime('%H-%M-%S', time.localtime(time.time()))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_date_time(fmt="%Y-%m-%d %H:%M:%S"):
|
|
17
|
+
return time.strftime(fmt, time.localtime(time.time()))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def str_to_datetime(val: str, fmt="%Y-%m-%d %H:%M:%S"):
|
|
21
|
+
return time.strptime(val, fmt)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def str_to_timestamp(val: str, fmt="%Y-%m-%d %H:%M:%S"):
|
|
25
|
+
return time.mktime(time.strptime(val, fmt))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def timestamp_to_str(timestamp: int = time.time(), fmt="%Y-%m-%d %H:%M:%S"):
|
|
29
|
+
return time.strftime(fmt, time.localtime(timestamp))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_today_start_end(now: datetime.now()):
|
|
33
|
+
start = datetime(now.year, now.month, now.day, 0, 0, 0)
|
|
34
|
+
end = datetime(now.year, now.month, now.day, 23, 59, 59)
|
|
35
|
+
return start.strftime("%Y-%m-%d %H:%M:%S"), end.strftime("%Y-%m-%d %H:%M:%S")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_week_start_end(now: datetime.now()):
|
|
39
|
+
start = now - timedelta(days=now.weekday()) # 本周一
|
|
40
|
+
end = start + timedelta(days=6) # 本周日
|
|
41
|
+
return start.strftime("%Y-%m-%d 00:00:00"), end.strftime("%Y-%m-%d 23:59:59")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def time_diff_in_seconds(sub_head: str = get_date_time(), sub_end: str = get_date_time()):
|
|
45
|
+
start_ts = str_to_timestamp(sub_head)
|
|
46
|
+
end_ts = str_to_timestamp(sub_end)
|
|
47
|
+
return int(start_ts - end_ts)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def opt_time(base_time=None, days=0, hours=0, minutes=0, seconds=0, weeks=0, fmt="%Y-%m-%d %H:%M:%S"):
|
|
51
|
+
if base_time is None:
|
|
52
|
+
base_time = datetime.now()
|
|
53
|
+
elif isinstance(base_time, str):
|
|
54
|
+
base_time = datetime.strptime(base_time, fmt)
|
|
55
|
+
new_time = base_time + timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds, weeks=weeks)
|
|
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/cdebug.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from queue import Queue, Empty
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProgramInterceptor:
|
|
9
|
+
def __init__(self, command):
|
|
10
|
+
self.command = command
|
|
11
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
12
|
+
self.log_filename = f"program_io_log_{timestamp}.txt"
|
|
13
|
+
self.process = None
|
|
14
|
+
self.log_queue = Queue()
|
|
15
|
+
|
|
16
|
+
def start(self):
|
|
17
|
+
if self.command[0] == "--log":
|
|
18
|
+
# 启动日志写入线程
|
|
19
|
+
log_thread = threading.Thread(target=self._write_log_thread, daemon=True)
|
|
20
|
+
log_thread.start()
|
|
21
|
+
self.command = self.command[1:]
|
|
22
|
+
# 启动子进程
|
|
23
|
+
self.process = subprocess.Popen(
|
|
24
|
+
self.command,
|
|
25
|
+
stdin=subprocess.PIPE,
|
|
26
|
+
stdout=subprocess.PIPE,
|
|
27
|
+
stderr=subprocess.PIPE,
|
|
28
|
+
bufsize=0,
|
|
29
|
+
universal_newlines=True
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# 记录初始信息
|
|
33
|
+
self._enqueue_log("header", f"Command: {' '.join(self.command)}")
|
|
34
|
+
self._enqueue_log("header", f"Start time: {datetime.now()}")
|
|
35
|
+
self._enqueue_log("header", "-" * 50)
|
|
36
|
+
|
|
37
|
+
# 启动输出转发线程
|
|
38
|
+
stdout_thread = threading.Thread(
|
|
39
|
+
target=self._forward_stream,
|
|
40
|
+
args=(self.process.stdout, sys.stdout, "stdout"),
|
|
41
|
+
daemon=True
|
|
42
|
+
)
|
|
43
|
+
stderr_thread = threading.Thread(
|
|
44
|
+
target=self._forward_stream,
|
|
45
|
+
args=(self.process.stderr, sys.stderr, "stderr"),
|
|
46
|
+
daemon=True
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
stdout_thread.start()
|
|
50
|
+
stderr_thread.start()
|
|
51
|
+
|
|
52
|
+
# 主线程处理输入转发
|
|
53
|
+
try:
|
|
54
|
+
while True:
|
|
55
|
+
if self.process.poll() is not None:
|
|
56
|
+
break
|
|
57
|
+
|
|
58
|
+
# 读取用户输入
|
|
59
|
+
try:
|
|
60
|
+
user_input = sys.stdin.readline()
|
|
61
|
+
if not user_input: # EOF
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
# 记录输入
|
|
65
|
+
self._enqueue_log("stdin", user_input)
|
|
66
|
+
|
|
67
|
+
# 转发到子进程
|
|
68
|
+
try:
|
|
69
|
+
self.process.stdin.write(user_input)
|
|
70
|
+
self.process.stdin.flush()
|
|
71
|
+
except (BrokenPipeError, OSError):
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
except KeyboardInterrupt:
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
finally:
|
|
78
|
+
# 清理工作
|
|
79
|
+
self.process.terminate()
|
|
80
|
+
|
|
81
|
+
# 等待所有输出处理完成
|
|
82
|
+
stdout_thread.join(timeout=1)
|
|
83
|
+
stderr_thread.join(timeout=1)
|
|
84
|
+
|
|
85
|
+
# 记录结束信息
|
|
86
|
+
self._enqueue_log("header", "-" * 50)
|
|
87
|
+
self._enqueue_log("header", f"End time: {datetime.now()}")
|
|
88
|
+
self._enqueue_log("header", f"Exit code: {self.process.returncode}")
|
|
89
|
+
|
|
90
|
+
# 等待日志写入完成
|
|
91
|
+
self.log_queue.put(None) # 结束信号
|
|
92
|
+
if hasattr(self, "log_thread") and isinstance(self.log_thread, threading.Thread):
|
|
93
|
+
if self.log_thread.is_alive():
|
|
94
|
+
self.log_thread.join(timeout=2)
|
|
95
|
+
|
|
96
|
+
def _forward_stream(self, source, target, stream_name):
|
|
97
|
+
"""转发数据流并记录"""
|
|
98
|
+
for line in iter(source.readline, ''):
|
|
99
|
+
# 转发到目标
|
|
100
|
+
target.write(line)
|
|
101
|
+
target.flush()
|
|
102
|
+
|
|
103
|
+
# 记录输出
|
|
104
|
+
self._enqueue_log(stream_name, line)
|
|
105
|
+
|
|
106
|
+
def _enqueue_log(self, stream_type, content):
|
|
107
|
+
"""将日志内容放入队列"""
|
|
108
|
+
self.log_queue.put((stream_type, content))
|
|
109
|
+
|
|
110
|
+
def _write_log_thread(self):
|
|
111
|
+
"""日志写入线程"""
|
|
112
|
+
with open(self.log_filename, 'w', encoding='utf-8') as log_file:
|
|
113
|
+
while True:
|
|
114
|
+
try:
|
|
115
|
+
item = self.log_queue.get(timeout=0.5)
|
|
116
|
+
if item is None: # 结束信号
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
stream_type, content = item
|
|
120
|
+
|
|
121
|
+
if stream_type == 'stderr': continue
|
|
122
|
+
|
|
123
|
+
if stream_type == "header":
|
|
124
|
+
log_file.write(content + "\n")
|
|
125
|
+
else:
|
|
126
|
+
direction = ">>>" if stream_type == "stdin" else "<<<"
|
|
127
|
+
log_file.write(f"{direction} {stream_type}: {content}")
|
|
128
|
+
|
|
129
|
+
log_file.flush()
|
|
130
|
+
|
|
131
|
+
except Empty:
|
|
132
|
+
if self.process.poll() is not None:
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def main():
|
|
137
|
+
if len(sys.argv) < 2:
|
|
138
|
+
print("Usage: cdebug.py <program> [args...]")
|
|
139
|
+
print("Example: cdebug.py python script.py arg1 arg2")
|
|
140
|
+
sys.exit(1)
|
|
141
|
+
interceptor = ProgramInterceptor(sys.argv[1:])
|
|
142
|
+
interceptor.start()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
main()
|
ctools/cid.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ctools.util.snow_id import SnowId
|
|
2
|
+
|
|
3
|
+
idWorker = SnowId(1, 2, 0)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_snowflake_id():
|
|
7
|
+
return idWorker.get_id()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_snowflake_id_str():
|
|
11
|
+
return str(get_snowflake_id())
|
|
12
|
+
|
|
13
|
+
def get_random_str(size: int = 10) -> str:
|
|
14
|
+
import random
|
|
15
|
+
return "".join(random.sample('abcdefghjklmnpqrstuvwxyz123456789', size))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_uuid() -> str:
|
|
19
|
+
import uuid
|
|
20
|
+
return str(uuid.uuid1()).replace("-", "")
|
ctools/{b64.py → cipher/b64.py}
RENAMED
ctools/cipher/czip.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
__author__ = 'haoyang'
|
|
4
|
+
__date__ = '2025/1/24 08:48'
|
|
5
|
+
|
|
6
|
+
import io
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
import pyzipper
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
target_directory = '/Users/haoyang/Desktop/知识库文件'
|
|
14
|
+
zip_password = None
|
|
15
|
+
process_directory_to_single_zip(target_directory, zip_password, "knowledge_base")
|
|
16
|
+
|
|
17
|
+
files_to_compress = [
|
|
18
|
+
'/path/to/file1.txt',
|
|
19
|
+
'/path/to/file2.pdf',
|
|
20
|
+
'/path/to/file3.jpg'
|
|
21
|
+
]
|
|
22
|
+
output_directory = '/Users/haoyang/Desktop'
|
|
23
|
+
compress_specific_files(files_to_compress, output_directory, zip_password, "my_files")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_zip_with_files(file_dict, password=None) -> io.BytesIO:
|
|
28
|
+
"""Compress multiple files into a single password-protected ZIP archive in memory.
|
|
29
|
+
Args:
|
|
30
|
+
file_dict: Dictionary of {filename: file_content} pairs
|
|
31
|
+
filename = os.path.relpath(file_path, start=root_dir) # 相对路径获取, 用于在 zip 内的路径定位
|
|
32
|
+
password: Optional password for the ZIP file
|
|
33
|
+
Returns:
|
|
34
|
+
BytesIO object containing the ZIP file
|
|
35
|
+
"""
|
|
36
|
+
zip_buffer = io.BytesIO()
|
|
37
|
+
try:
|
|
38
|
+
if password:
|
|
39
|
+
with pyzipper.AESZipFile(zip_buffer, 'w', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as zipf:
|
|
40
|
+
zipf.setpassword(password.encode('utf-8'))
|
|
41
|
+
for filename, content in file_dict.items():
|
|
42
|
+
zipf.writestr(filename, content)
|
|
43
|
+
else:
|
|
44
|
+
with pyzipper.ZipFile(zip_buffer, 'w', compression=pyzipper.ZIP_DEFLATED) as zipf:
|
|
45
|
+
for filename, content in file_dict.items():
|
|
46
|
+
zipf.writestr(filename, content)
|
|
47
|
+
zip_buffer.seek(0)
|
|
48
|
+
return zip_buffer
|
|
49
|
+
except Exception as e:
|
|
50
|
+
zip_buffer.close()
|
|
51
|
+
raise e
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def process_directory_to_single_zip(root_dir, password=None, zip_name=None):
|
|
55
|
+
"""Walk through directory and compress all files into a single ZIP.
|
|
56
|
+
Args:
|
|
57
|
+
root_dir: Root directory to scan for files
|
|
58
|
+
password: Optional password for the ZIP file
|
|
59
|
+
zip_name: Base name for the ZIP file (without extension)
|
|
60
|
+
"""
|
|
61
|
+
file_dict = {}
|
|
62
|
+
for dirpath, _, filenames in os.walk(root_dir):
|
|
63
|
+
for filename in filenames:
|
|
64
|
+
file_path = os.path.join(dirpath, filename)
|
|
65
|
+
try:
|
|
66
|
+
with open(file_path, 'rb') as f:
|
|
67
|
+
rel_path = os.path.relpath(file_path, start=root_dir)
|
|
68
|
+
file_dict[rel_path] = f.read()
|
|
69
|
+
except Exception as e:
|
|
70
|
+
print(f"Error reading {file_path}: {str(e)}")
|
|
71
|
+
if not file_dict:
|
|
72
|
+
print("No files found to compress.")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
zip_buffer = create_zip_with_files(file_dict, password)
|
|
77
|
+
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
|
78
|
+
if zip_name:
|
|
79
|
+
base_name = f"{zip_name}_{timestamp}.zip"
|
|
80
|
+
else:
|
|
81
|
+
base_name = f"archive_{timestamp}.zip"
|
|
82
|
+
output_path = os.path.join(root_dir, base_name)
|
|
83
|
+
with open(output_path, 'wb') as out_file:
|
|
84
|
+
out_file.write(zip_buffer.read())
|
|
85
|
+
print(f"Created single archive: {output_path}")
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print(f"Error creating ZIP archive: {str(e)}")
|
|
88
|
+
finally:
|
|
89
|
+
if 'zip_buffer' in locals(): zip_buffer.close()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def compress_specific_files(file_paths: [], output_dir: str, password=None, zip_name=None):
|
|
93
|
+
"""Compress multiple specified files into a single ZIP archive.
|
|
94
|
+
Args:
|
|
95
|
+
file_paths: List of absolute file paths to compress
|
|
96
|
+
output_dir: Directory where the ZIP file will be saved
|
|
97
|
+
password: Optional password for the ZIP file
|
|
98
|
+
zip_name: Base name for the ZIP file (without extension)
|
|
99
|
+
"""
|
|
100
|
+
if not file_paths:
|
|
101
|
+
print("No files specified to compress.")
|
|
102
|
+
return
|
|
103
|
+
file_dict = {}
|
|
104
|
+
for file_path in file_paths:
|
|
105
|
+
if not os.path.isfile(file_path):
|
|
106
|
+
print(f"Warning: {file_path} is not a file or doesn't exist. Skipping.")
|
|
107
|
+
continue
|
|
108
|
+
try:
|
|
109
|
+
with open(file_path, 'rb') as f:
|
|
110
|
+
filename_in_zip = os.path.basename(file_path)
|
|
111
|
+
file_dict[filename_in_zip] = f.read()
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"Error reading {file_path}: {str(e)}")
|
|
114
|
+
if not file_dict:
|
|
115
|
+
print("No valid files found to compress.")
|
|
116
|
+
return
|
|
117
|
+
try:
|
|
118
|
+
zip_buffer = create_zip_with_files(file_dict, password)
|
|
119
|
+
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
|
|
120
|
+
if zip_name:
|
|
121
|
+
base_name = f"{zip_name}_{timestamp}.zip"
|
|
122
|
+
else:
|
|
123
|
+
first_file = os.path.basename(file_paths[0])
|
|
124
|
+
base_name = f"{os.path.splitext(first_file)[0]}_{timestamp}.zip"
|
|
125
|
+
output_path = os.path.join(output_dir, base_name)
|
|
126
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
127
|
+
with open(output_path, 'wb') as out_file:
|
|
128
|
+
out_file.write(zip_buffer.read())
|
|
129
|
+
print(f"Created archive: {output_path}")
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"Error creating ZIP archive: {str(e)}")
|
|
132
|
+
finally:
|
|
133
|
+
if 'zip_buffer' in locals(): zip_buffer.close()
|
ctools/cipher/rsa.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
|
|
3
|
+
from Crypto.Cipher import PKCS1_OAEP
|
|
4
|
+
from Crypto.Hash import SHA256
|
|
5
|
+
from Crypto.PublicKey import RSA
|
|
6
|
+
from Crypto.Signature import pkcs1_15
|
|
7
|
+
|
|
8
|
+
from ctools import cjson, path_info
|
|
9
|
+
|
|
10
|
+
ENCRYPT_CHUNK_SIZE = 245
|
|
11
|
+
decrypt_CHUNK_SIZE = 512
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_rsa_keypair(bits=2048):
|
|
15
|
+
key = RSA.generate(bits)
|
|
16
|
+
private_key = key.export_key() # 导出私钥
|
|
17
|
+
public_key = key.publickey().export_key() # 导出公钥
|
|
18
|
+
with open("private_key.pem", "wb") as f:
|
|
19
|
+
f.write(private_key)
|
|
20
|
+
with open("public_key.pem", "wb") as f:
|
|
21
|
+
f.write(public_key)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def loadLicenseInfo(auth_code):
|
|
25
|
+
with open(path_info.get_app_path() + '/keys/license.key', 'r') as pri:
|
|
26
|
+
decrypt_message = decrypt(auth_code.strip(), pri.read())
|
|
27
|
+
return cjson.loads(decrypt_message)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# 加密函数
|
|
31
|
+
def encrypt(msg, public_key):
|
|
32
|
+
parts = b''
|
|
33
|
+
public_key = RSA.import_key(public_key)
|
|
34
|
+
cipher = PKCS1_OAEP.new(public_key)
|
|
35
|
+
for i in range(0, len(msg), ENCRYPT_CHUNK_SIZE):
|
|
36
|
+
parts += cipher.encrypt(msg[i:i + ENCRYPT_CHUNK_SIZE].encode())
|
|
37
|
+
encrypted_base64 = base64.b64encode(parts)
|
|
38
|
+
return encrypted_base64.decode()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# 解密函数
|
|
42
|
+
def decrypt(msg, private_key):
|
|
43
|
+
parts = b''
|
|
44
|
+
public_key = RSA.import_key(private_key)
|
|
45
|
+
cipher = PKCS1_OAEP.new(public_key)
|
|
46
|
+
encrypted_bytes = base64.b64decode(msg)
|
|
47
|
+
for i in range(0, len(encrypted_bytes), decrypt_CHUNK_SIZE):
|
|
48
|
+
parts += cipher.decrypt(encrypted_bytes[i:i + decrypt_CHUNK_SIZE])
|
|
49
|
+
return parts.decode()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# 验签
|
|
53
|
+
def verify_sign(msg, public_key, sign):
|
|
54
|
+
public_key = RSA.import_key(public_key)
|
|
55
|
+
hash_message = SHA256.new(msg.encode())
|
|
56
|
+
try:
|
|
57
|
+
pkcs1_15.new(public_key).verify(hash_message, base64.b64decode(sign.encode()))
|
|
58
|
+
return True
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print('签名验证失败: {}'.format(e))
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# 签名
|
|
65
|
+
def sign_msg(msg, private_key):
|
|
66
|
+
private_key = RSA.import_key(private_key)
|
|
67
|
+
hash_message = SHA256.new(msg.encode())
|
|
68
|
+
signature = pkcs1_15.new(private_key).sign(hash_message)
|
|
69
|
+
return base64.b64encode(signature).decode()
|
|
70
|
+
|
|
71
|
+
# with open(work_path.get_current_path() + '/private_key.pem', 'r') as key:
|
|
72
|
+
# key = key.read()
|
|
73
|
+
# sign = sign_msg(key, key)
|
|
74
|
+
# with open(work_path.get_current_path() + '/public_key.pem', 'r') as pub:
|
|
75
|
+
# print(verify_sign(key, pub.read(), sign+'123'))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import hmac
|
|
3
3
|
|
|
4
|
-
global_key = '
|
|
4
|
+
global_key = 'gomyck2014'
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def generate_signature(file_path, key: str = global_key):
|
|
@@ -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()
|