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.
Files changed (107) hide show
  1. ctools/__init__.py +21 -0
  2. ctools/ai/__init__.py +4 -0
  3. ctools/ai/llm_chat.py +184 -0
  4. ctools/ai/llm_client.py +163 -0
  5. ctools/ai/llm_exception.py +17 -0
  6. ctools/ai/mcp/__init__.py +4 -0
  7. ctools/ai/mcp/mcp_client.py +326 -0
  8. ctools/ai/tools/__init__.py +4 -0
  9. ctools/ai/tools/json_extract.py +202 -0
  10. ctools/ai/tools/quick_tools.py +149 -0
  11. ctools/ai/tools/think_process.py +11 -0
  12. ctools/ai/tools/tool_use_xml_parse.py +40 -0
  13. ctools/ai/tools/xml_extract.py +15 -0
  14. ctools/application.py +50 -47
  15. ctools/aspect.py +65 -0
  16. ctools/auto/__init__.py +4 -0
  17. ctools/{browser_element_tools.py → auto/browser_element.py} +18 -8
  18. ctools/{plan_area_tools.py → auto/plan_area.py} +5 -7
  19. ctools/{pty_tools.py → auto/pty_process.py} +6 -3
  20. ctools/{resource_bundle_tools.py → auto/resource_bundle.py} +4 -4
  21. ctools/{screenshot_tools.py → auto/screenshot.py} +7 -6
  22. ctools/{win_canvas.py → auto/win_canvas.py} +10 -4
  23. ctools/{win_control.py → auto/win_control.py} +14 -5
  24. ctools/call.py +34 -49
  25. ctools/cdate.py +84 -0
  26. ctools/cdebug.py +146 -0
  27. ctools/cid.py +20 -0
  28. ctools/cipher/__init__.py +4 -0
  29. ctools/{aes_tools.py → cipher/aes_util.py} +10 -0
  30. ctools/{b64.py → cipher/b64.py} +2 -0
  31. ctools/cipher/czip.py +133 -0
  32. ctools/cipher/rsa.py +75 -0
  33. ctools/{sign.py → cipher/sign.py} +2 -1
  34. ctools/{sm_tools.py → cipher/sm_util.py} +20 -4
  35. ctools/cjson.py +10 -10
  36. ctools/cron_lite.py +109 -97
  37. ctools/database/__init__.py +4 -0
  38. ctools/{database.py → database/database.py} +93 -26
  39. ctools/dict_wrapper.py +21 -0
  40. ctools/ex.py +4 -0
  41. ctools/geo/__init__.py +4 -0
  42. ctools/geo/coord_trans.py +127 -0
  43. ctools/geo/douglas_rarefy.py +139 -0
  44. ctools/metrics.py +56 -63
  45. ctools/office/__init__.py +4 -0
  46. ctools/office/cword.py +30 -0
  47. ctools/{word_fill.py → office/word_fill.py} +3 -6
  48. ctools/patch.py +88 -0
  49. ctools/{work_path.py → path_info.py} +34 -2
  50. ctools/pkg/__init__.py +4 -0
  51. ctools/pkg/dynamic_imp.py +38 -0
  52. ctools/pools/__init__.py +4 -0
  53. ctools/pools/process_pool.py +41 -0
  54. ctools/{thread_pool.py → pools/thread_pool.py} +13 -4
  55. ctools/similar.py +25 -0
  56. ctools/stream/__init__.py +4 -0
  57. ctools/stream/ckafka.py +164 -0
  58. ctools/stream/credis.py +127 -0
  59. ctools/{mqtt_utils.py → stream/mqtt_utils.py} +20 -12
  60. ctools/sys_info.py +36 -13
  61. ctools/sys_log.py +46 -27
  62. ctools/util/__init__.py +4 -0
  63. ctools/util/cftp.py +76 -0
  64. ctools/util/cklock.py +118 -0
  65. ctools/util/config_util.py +52 -0
  66. ctools/util/env_config.py +63 -0
  67. ctools/{html_soup.py → util/html_soup.py} +0 -7
  68. ctools/{http_utils.py → util/http_util.py} +4 -2
  69. ctools/{images_tools.py → util/image_process.py} +10 -1
  70. ctools/util/jb_cut.py +54 -0
  71. ctools/{id_worker_tools.py → util/snow_id.py} +8 -23
  72. ctools/web/__init__.py +4 -0
  73. ctools/web/aio_web_server.py +186 -0
  74. ctools/web/api_result.py +56 -0
  75. ctools/web/bottle_web_base.py +239 -0
  76. ctools/web/bottle_webserver.py +191 -0
  77. ctools/web/bottle_websocket.py +79 -0
  78. ctools/web/ctoken.py +103 -0
  79. ctools/{download_tools.py → web/download_util.py} +14 -12
  80. ctools/web/params_util.py +46 -0
  81. ctools/{upload_tools.py → web/upload_util.py} +3 -2
  82. gomyck_tools-1.4.7.dist-info/METADATA +70 -0
  83. gomyck_tools-1.4.7.dist-info/RECORD +88 -0
  84. {gomyck_tools-1.0.0.dist-info → gomyck_tools-1.4.7.dist-info}/WHEEL +1 -1
  85. gomyck_tools-1.4.7.dist-info/licenses/LICENSE +13 -0
  86. ctools/bashPath.py +0 -13
  87. ctools/bottle_server.py +0 -49
  88. ctools/console.py +0 -55
  89. ctools/date_utils.py +0 -44
  90. ctools/enums.py +0 -4
  91. ctools/excelOpt.py +0 -36
  92. ctools/imgDialog.py +0 -44
  93. ctools/license.py +0 -37
  94. ctools/log.py +0 -28
  95. ctools/mvc.py +0 -56
  96. ctools/obj.py +0 -20
  97. ctools/pacth.py +0 -73
  98. ctools/ssh.py +0 -9
  99. ctools/strDiff.py +0 -20
  100. ctools/string_tools.py +0 -101
  101. ctools/token_tools.py +0 -13
  102. ctools/wordFill.py +0 -24
  103. gomyck_tools-1.0.0.dist-info/METADATA +0 -20
  104. gomyck_tools-1.0.0.dist-info/RECORD +0 -54
  105. /ctools/{word_fill_entity.py → office/word_fill_entity.py} +0 -0
  106. /ctools/{compile_tools.py → util/compile_util.py} +0 -0
  107. {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
- from ctools import thread_pool, win_canvas, application, string_tools
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" % string_tools.get_snowflake_id())
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
- decorator to initialize a function once
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
- nonlocal initialized, res
18
- if not initialized:
19
- res = func(*args, **kwargs)
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
- decorator to initialize a function automic
31
- :param func: function to be initialized
32
- :return: the real decorator for return the result
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 res
38
-
27
+ return _cache[key]
39
28
  return wrapper
40
29
 
41
-
42
- def schd(interval_seconds, start_by_call: bool = False):
43
- start_flag = False
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
- 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
-
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
- nonlocal run_flag
61
- if not run_flag:
62
- scheduler.enter(interval_seconds, 1, job)
63
- run_flag = True
64
- scheduler.run()
65
- else:
66
- func(*args, **kwargs)
67
-
68
- if not start_by_call: threading.Thread(target=wrapper, daemon=True).start()
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("-", "")
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/6/11 09:20'
@@ -1,6 +1,16 @@
1
1
  from cryptography.fernet import Fernet
2
2
 
3
3
 
4
+ def generate_key():
5
+ """
6
+ 生成 AES key
7
+ Returns 32 bytes key
8
+ -------
9
+ """
10
+ key = Fernet.generate_key()
11
+ return key.decode()
12
+
13
+
4
14
  def aes_encrypt(sec_key, plaintext):
5
15
  """
6
16
  aes加密
@@ -1,7 +1,9 @@
1
1
  import base64
2
2
 
3
+
3
4
  def encode(param: str):
4
5
  return base64.b64encode(param.encode('UTF-8')).decode('UTF-8')
5
6
 
7
+
6
8
  def decode(param: str):
7
9
  return base64.b64decode(param.encode('UTF-8')).decode('UTF-8')
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 = 'hylink2014'
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()