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
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/7/15 11:03'
5
+
6
+ import importlib
7
+ import pkgutil
8
+
9
+
10
+ def load_modules_from_package(package, exclude=None, recursive=True):
11
+ """
12
+ 递归加载指定包下所有模块(不包括包本身)
13
+
14
+ :param package: 要加载模块的包对象(如 mypkg.plugins)
15
+ :param exclude: 排除的模块完整路径列表(如 ['mypkg.plugins.demo.mod2'])
16
+ :param recursive: 是否递归子包
17
+ :return: 模块列表(不含子包本身,只包含模块)
18
+ """
19
+ if exclude is None: exclude = []
20
+ modules = []
21
+ for finder, modname, ispkg in pkgutil.iter_modules(package.__path__):
22
+ full_modname = f"{package.__name__}.{modname}"
23
+ if ispkg and recursive:
24
+ try:
25
+ subpkg = importlib.import_module(full_modname)
26
+ modules.extend(load_modules_from_package(subpkg, exclude, recursive))
27
+ except Exception as e:
28
+ print(f"递归子包 {full_modname} 失败:{e}")
29
+ continue
30
+ if full_modname in exclude:
31
+ continue
32
+ try:
33
+ module = importlib.import_module(full_modname)
34
+ modules.append(module)
35
+ except Exception as e:
36
+ print(f"!!!!!!加载模块 {full_modname} 失败:{e}!!!!!!")
37
+ continue
38
+ return modules
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/6/11 09:18'
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2024/9/20 12:00'
5
+
6
+ import os
7
+ import time
8
+ from concurrent.futures import ProcessPoolExecutor
9
+
10
+ from ctools import call
11
+
12
+ _process_pool: ProcessPoolExecutor = None
13
+
14
+
15
+ @call.init
16
+ def init():
17
+ global _process_pool
18
+ max_workers = min(32, os.cpu_count()) # 最多 32 个
19
+ _process_pool = ProcessPoolExecutor(max_workers=max_workers)
20
+
21
+
22
+ def cb(f, callback):
23
+ exc = f.exception()
24
+ if exc:
25
+ if callback: callback(exc)
26
+ raise exc
27
+ else:
28
+ if callback: callback(f.result())
29
+
30
+
31
+ def submit(func, *args, callback=None, **kwargs):
32
+ if _process_pool is None: raise Exception('process pool is not init')
33
+ future = _process_pool.submit(func, *args, **kwargs)
34
+ future.add_done_callback(lambda f: cb(f, callback))
35
+ time.sleep(0.01)
36
+ return future
37
+
38
+
39
+ def shutdown(wait=True):
40
+ if _process_pool is None: raise Exception('process pool is not init')
41
+ _process_pool.shutdown(wait=wait)
@@ -1,30 +1,39 @@
1
1
  import os
2
+ import threading
2
3
  import time
3
4
  from concurrent.futures import ThreadPoolExecutor
4
- import threading
5
+
5
6
  from ctools import call
6
7
 
7
8
  thread_local = threading.local()
8
9
 
9
10
  _threadPool: ThreadPoolExecutor = None
10
11
 
12
+
11
13
  @call.init
12
14
  def init():
13
15
  global _threadPool
14
- max_work_num = min(32, (os.cpu_count() or 1) + 4) # 最多 32 个
15
- _threadPool = ThreadPoolExecutor(max_workers= max_work_num, thread_name_prefix='ck-')
16
+ max_work_num = min(32, (os.cpu_count() or 1) + 4) # 最多 32 个
17
+ _threadPool = ThreadPoolExecutor(max_workers=max_work_num, thread_name_prefix='ck-')
18
+
16
19
 
17
20
  def cb(f, callback):
18
21
  exc = f.exception()
19
22
  if exc:
23
+ print(f"Task failed: {exc}")
20
24
  if callback: callback(exc)
21
- raise exc
22
25
  else:
23
26
  if callback: callback(f.result())
24
27
 
28
+
25
29
  def submit(func, *args, callback=None, **kwargs):
26
30
  if _threadPool is None: raise Exception('thread pool is not init')
27
31
  future = _threadPool.submit(func, *args, **kwargs)
28
32
  future.add_done_callback(lambda f: cb(f, callback))
29
33
  time.sleep(0.01)
30
34
  return future
35
+
36
+
37
+ def shutdown(wait=True):
38
+ if _threadPool is None: raise Exception('thread pool is not init')
39
+ _threadPool.shutdown(wait=wait)
ctools/similar.py ADDED
@@ -0,0 +1,25 @@
1
+ from fuzzywuzzy import fuzz, process
2
+
3
+
4
+ def best_match(query: str, choices: list[str], score_cutoff: int = 70):
5
+ """
6
+ 获取最接近 query 的匹配项
7
+ :return: (匹配项, 相似度得分) 或 None
8
+ """
9
+ return process.extractOne(query, choices, scorer=fuzz.ratio, score_cutoff=score_cutoff)
10
+
11
+
12
+ def all_matches(query: str, choices: list[str], limit: int = 5):
13
+ """
14
+ 获取多个相似匹配项
15
+ :return: [(匹配项, 相似度得分), ...]
16
+ """
17
+ return process.extract(query, choices, scorer=fuzz.ratio, limit=limit)
18
+
19
+
20
+ def is_similar(s1: str, s2: str, threshold: int = 85):
21
+ """
22
+ 判断两个字符串是否相似
23
+ :return: True / False
24
+ """
25
+ return fuzz.ratio(s1, s2) >= threshold
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/6/11 09:21'
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2024/9/5 10:39'
5
+
6
+ import time
7
+ from threading import Thread
8
+
9
+ from kafka import KafkaProducer, errors, KafkaConsumer
10
+ from kafka.producer.future import FutureRecordMetadata
11
+
12
+ from ctools.cjson import dumps
13
+
14
+ """
15
+ import time
16
+ from datetime import datetime
17
+
18
+ from ctools import thread_pool, string_tools
19
+ from ctools.ckafka import CKafka
20
+
21
+ c = CKafka(kafka_url='192.168.3.160:9094', secure=True)
22
+
23
+ producer = c.init_producer()
24
+ consumer = c.init_consumer(enable_auto_commit=False)
25
+
26
+ def send_msg():
27
+ while True:
28
+ command = input('发送消息: Y/n \n')
29
+ if command.strip() not in ['N', 'n']:
30
+ producer.send_msg('jqxx', '{{"jqid": "{}", "xxxx": "{}"}}'.format(string_tools.get_snowflake_id(), datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')))
31
+ else:
32
+ break
33
+
34
+ thread_pool.submit(send_msg)
35
+
36
+ def consumer_callback(msg):
37
+ print(msg)
38
+ return True
39
+
40
+ consumer.receive_msg('jqxx', callBack=consumer_callback)
41
+
42
+ while True: time.sleep(1)
43
+ """
44
+
45
+
46
+ class KafkaInstance:
47
+ def __init__(self, producer: KafkaProducer, consumer: KafkaConsumer):
48
+ self.start_consumer = False
49
+ self.quited = False
50
+ self.producer = producer
51
+ self.consumer = consumer
52
+ self.consumer_callback = {"topic_key": []}
53
+
54
+ # FutureRecordMetadata 可以添加回调, 来监听是否发送成功
55
+ # r.add_callback(lambda x: print(x))
56
+ # r.get() 可以同步获取结果
57
+ def send_msg(self, topic, msg, key: str = None, partition: int = None) -> FutureRecordMetadata:
58
+ if self.producer is None: raise RuntimeError("Producer is not initialized")
59
+ if self.quited: return
60
+ return self.producer.send(topic=topic, value=msg, key=None if key is None else key.encode('utf-8'), partition=partition)
61
+
62
+ def receive_msg(self, topics: str, callBack=print):
63
+ if self.consumer is None: raise RuntimeError("Consumer is not initialized")
64
+ for topic in topics.split(','):
65
+ if topic not in self.consumer_callback.keys():
66
+ self.consumer_callback[topic] = []
67
+ self.consumer.subscribe(self.consumer_callback.keys())
68
+ self.consumer_callback[topic].append(callBack)
69
+ if not self.start_consumer:
70
+ t = Thread(target=self._start_consumer_poll, daemon=True)
71
+ t.start()
72
+
73
+ def _start_consumer_poll(self):
74
+ self.start_consumer = True
75
+ for msg in self.consumer:
76
+ if self.quited: break
77
+ funcList = []
78
+ begin_time = time.time()
79
+ for func in self.consumer_callback[msg.topic]:
80
+ if self.quited: break
81
+ res = func(msg)
82
+ if not self.consumer.config['enable_auto_commit'] and res: self.consumer.commit()
83
+ funcList.append(func.__name__)
84
+ end_time = time.time()
85
+ if end_time - begin_time > 1: print(f"kafka consume too slow!!! {funcList} time cost: ", f'{round(end_time - begin_time, 2)}s')
86
+ funcList.clear()
87
+
88
+ def shutdown(self):
89
+ self.quited = True
90
+ try:
91
+ self.consumer.close()
92
+ except Exception:
93
+ pass
94
+ try:
95
+ self.producer.close()
96
+ except Exception:
97
+ pass
98
+
99
+
100
+ class CKafka:
101
+
102
+ def __init__(self, kafka_url: str = '127.0.0.1:9092', secure: bool = False, username: str = 'client', password: str = 'hylink_user_password'):
103
+ self.kafka_url = kafka_url
104
+ self.secure = secure
105
+ self.username = username
106
+ self.password = password
107
+
108
+ def init_producer(self, acks=1) -> KafkaInstance:
109
+ print("[ Producer ] Connecting to Kafka [{}]".format(self.kafka_url))
110
+ for i in range(0, 6):
111
+ try:
112
+ if self.secure:
113
+ producer = KafkaProducer(
114
+ acks=acks,
115
+ bootstrap_servers=self.kafka_url,
116
+ value_serializer=lambda x: dumps(x).encode('utf-8'),
117
+ sasl_plain_username=self.username,
118
+ sasl_plain_password=self.password,
119
+ security_protocol='SASL_PLAINTEXT',
120
+ sasl_mechanism='PLAIN'
121
+ )
122
+ else:
123
+ producer = KafkaProducer(
124
+ acks=acks,
125
+ bootstrap_servers=self.kafka_url,
126
+ value_serializer=lambda x: dumps(x).encode('utf-8')
127
+ )
128
+ print("[ Producer ] Success Connected to Kafka [{}]".format(self.kafka_url))
129
+ return KafkaInstance(producer=producer, consumer=None)
130
+ except errors.NoBrokersAvailable:
131
+ print("[ Producer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
132
+ time.sleep(3)
133
+ raise RuntimeError("[ Producer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
134
+
135
+ def init_consumer(self, client_id: str = 'ck-py-kafka-consumer', consumer_group: str = 'ck-py-kafka-consumer', enable_auto_commit: bool = True) -> KafkaInstance:
136
+ print("[ Consumer ] Connecting to Kafka [{}]".format(self.kafka_url))
137
+ for i in range(0, 6):
138
+ try:
139
+ if self.secure:
140
+ consumer = KafkaConsumer(
141
+ client_id=client_id,
142
+ group_id=consumer_group,
143
+ enable_auto_commit=enable_auto_commit,
144
+ bootstrap_servers=self.kafka_url,
145
+ value_deserializer=lambda x: x.decode('utf-8'),
146
+ sasl_plain_username=self.username,
147
+ sasl_plain_password=self.password,
148
+ security_protocol='SASL_PLAINTEXT',
149
+ sasl_mechanism='PLAIN'
150
+ )
151
+ else:
152
+ consumer = KafkaProducer(
153
+ client_id=client_id,
154
+ group_id=consumer_group,
155
+ enable_auto_commit=enable_auto_commit,
156
+ bootstrap_servers=self.kafka_url,
157
+ value_deserializer=lambda x: x.decode('utf-8')
158
+ )
159
+ print("[ Consumer ] Success Connected to Kafka [{}]".format(self.kafka_url))
160
+ return KafkaInstance(producer=None, consumer=consumer)
161
+ except errors.NoBrokersAvailable:
162
+ print("[ Consumer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
163
+ time.sleep(3)
164
+ raise RuntimeError("[ Consumer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/2/14 11:09'
5
+
6
+ import redis
7
+ from redis import Redis
8
+
9
+ from ctools import cdate, cid
10
+ from ctools.pools import thread_pool
11
+
12
+ # 最后一次连接的redis
13
+ _ck_redis: Redis = None
14
+
15
+ def get_redis(): return _ck_redis
16
+
17
+ def init_pool(host: str = 'localhost', port: int = 6379, db: int = 0, password: str = None,
18
+ username: str = None, decode_responses: bool = True, max_connections: int = 75,
19
+ health_check_interval: int = 30, retry_count: int = 3) -> Redis:
20
+ for attempt in range(retry_count):
21
+ try:
22
+ r: Redis = redis.StrictRedis(
23
+ host=host, port=port, db=db,
24
+ username=username, password=password,
25
+ retry_on_timeout=True,
26
+ max_connections=max_connections,
27
+ decode_responses=decode_responses,
28
+ health_check_interval=health_check_interval,
29
+ socket_connect_timeout=5,
30
+ socket_timeout=5
31
+ )
32
+ if r.ping():
33
+ print('CRedis connect {} {} success!'.format(host, port))
34
+ global _ck_redis
35
+ _ck_redis = r
36
+ return _ck_redis
37
+ except redis.ConnectionError as e:
38
+ if attempt == retry_count - 1:
39
+ raise Exception(f"Failed to connect to Redis after {retry_count} attempts: {str(e)}")
40
+ print(f"Connection attempt {attempt + 1} failed, retrying...")
41
+
42
+
43
+ def add_lock(r: Redis, key: str, timeout: int = 30):
44
+ if r.exists(key):
45
+ expire_time = r.get(key)
46
+ if expire_time and cdate.time_diff_in_seconds(expire_time, cdate.get_date_time()) > 0:
47
+ return False
48
+ else:
49
+ r.delete(key)
50
+ return r.set(key, cdate.opt_time(seconds=timeout), nx=True, ex=timeout) is not None
51
+
52
+
53
+ def remove_lock(r: Redis, key: str):
54
+ r.delete(key)
55
+
56
+
57
+ def subscribe(r: Redis, channel_name, callback):
58
+ def thread_func():
59
+ pubsub = r.pubsub()
60
+ pubsub.subscribe(channel_name)
61
+ for message in pubsub.listen():
62
+ callback(message)
63
+
64
+ thread_pool.submit(thread_func)
65
+
66
+
67
+ def _process_pending_messages(r: Redis, stream_name: str, group_name: str, consumer_name: str, callback):
68
+ """
69
+ 处理未确认的消息
70
+ :param r: Redis 连接
71
+ :param stream_name: 流名称
72
+ :param group_name: 消费者组名称
73
+ :param consumer_name: 消费者名称
74
+ :param callback: 消息处理回调函数
75
+ """
76
+ # 检查未确认的消息
77
+ pending_messages = r.xpending(stream_name, group_name)
78
+ if pending_messages['pending'] > 0:
79
+ print(f"Found {pending_messages['pending']} pending messages.")
80
+ # 获取未确认的消息列表
81
+ pending_list = r.xpending_range(stream_name, group_name, min='-', max='+', count=pending_messages['pending'])
82
+ for message in pending_list:
83
+ message_id = message['message_id']
84
+ claimed_messages = r.xclaim(stream_name, group_name, consumer_name, min_idle_time=0, message_ids=[message_id])
85
+ if claimed_messages:
86
+ # 处理消息
87
+ for claimed_message in claimed_messages:
88
+ message_id, data = claimed_message
89
+ print(f"Processing pending message: {message_id}, data: {data}")
90
+ try:
91
+ if callback(message_id, data):
92
+ r.xack(stream_name, group_name, message_id)
93
+ except Exception as e:
94
+ print(f"Error processing message {message_id}: {e}")
95
+ else:
96
+ print("No pending messages found.")
97
+
98
+
99
+ def stream_subscribe(r: Redis, stream_name, group_name, callback, from_id: str = '$', noack: bool = False):
100
+ def thread_func():
101
+ try:
102
+ # $表示从最后面消费, 0表示从开始消费
103
+ r.xgroup_create(name=stream_name, groupname=group_name, id=from_id, mkstream=True)
104
+ print(f"Consumer group '{group_name}' created successfully.")
105
+ except Exception as e:
106
+ if "already exists" in str(e):
107
+ print(f"Consumer group '{group_name}' already exists.")
108
+ else:
109
+ print(f"Error creating consumer group '{group_name}': {e}")
110
+ consumer_name = 'consumer-{}'.format(cid.get_uuid())
111
+ # 处理未确认的消息
112
+ _process_pending_messages(r, stream_name, group_name, consumer_name, callback)
113
+ while True:
114
+ messages = r.xreadgroup(group_name, consumer_name, {stream_name: '>'}, block=1000, noack=noack)
115
+ for message in messages:
116
+ try:
117
+ message_id, data = message[1][0]
118
+ res = callback(message_id, data)
119
+ if res: r.xack(stream_name, group_name, message_id)
120
+ except Exception as e:
121
+ print('stream_subscribe error: ', e)
122
+
123
+ thread_pool.submit(thread_func)
124
+
125
+
126
+ def stream_publish(r: Redis, stream_name, message):
127
+ r.xadd(stream_name, message)
@@ -1,12 +1,20 @@
1
1
  import time
2
+ from enum import Enum
2
3
  from typing import Dict
3
4
 
4
5
  from paho.mqtt import client as mqtt
5
6
  from paho.mqtt.enums import CallbackAPIVersion
6
7
 
7
- from business.common import DictToObj
8
- from business.common.constant import MQTTEvent
9
- from ctools import sys_log, cjson, string_tools, sys_info, date_utils, sm_tools, thread_pool
8
+ from ctools import cid, cdate
9
+ from ctools import sys_log, cjson, sys_info
10
+ from ctools.cipher import sm_util
11
+ from ctools.dict_wrapper import DictWrapper as DictToObj
12
+ from ctools.pools import thread_pool
13
+
14
+
15
+ class MQTTEvent(Enum):
16
+ pass
17
+
10
18
 
11
19
  '''
12
20
  MQTT服务使用示例:
@@ -37,9 +45,9 @@ class MQTTRequest:
37
45
 
38
46
  def __init__(self, **kwargs):
39
47
  self.event: str = kwargs.get('event')
40
- self.trace_id: str = kwargs.get('trace_id') if kwargs.get('trace_id') else string_tools.get_uuid()
48
+ self.trace_id: str = kwargs.get('trace_id') if kwargs.get('trace_id') else cid.get_uuid()
41
49
  self.client_id: str = kwargs.get('client_id')
42
- self.time: str = kwargs.get('time') if kwargs.get('time') else date_utils.get_date_time()
50
+ self.time: str = kwargs.get('time') if kwargs.get('time') else cdate.get_date_time()
43
51
  self.body = kwargs.get('body')
44
52
 
45
53
  def __str__(self):
@@ -55,7 +63,7 @@ class MQTTResponse:
55
63
  self.event: str = kwargs.get('event')
56
64
  self.trace_id: str = kwargs.get('trace_id')
57
65
  self.status: int = kwargs.get('status')
58
- self.time: str = kwargs.get('time') if kwargs.get('time') else date_utils.get_date_time()
66
+ self.time: str = kwargs.get('time') if kwargs.get('time') else cdate.get_date_time()
59
67
  self.msg: str = kwargs.get('msg')
60
68
  self.body = kwargs.get('body')
61
69
 
@@ -89,8 +97,8 @@ class MQTTUtils:
89
97
  will_msg = {
90
98
  "event": "offline",
91
99
  "client_id": self.client_id,
92
- "trace_id": string_tools.get_uuid(),
93
- "time": date_utils.get_date_time(),
100
+ "trace_id": cid.get_uuid(),
101
+ "time": cdate.get_date_time(),
94
102
  "body": {"type": "will_msg"}
95
103
  }
96
104
  self.client.will_set(self.publish_topic, cjson.dumps(will_msg), 2, False)
@@ -145,16 +153,16 @@ class MQTTUtils:
145
153
  def encrypt_body(self, body):
146
154
  if self.broker_encrypt_key:
147
155
  try:
148
- return sm_tools.encrypt_with_sm4(self.broker_encrypt_key, cjson.dumps(body))
156
+ return sm_util.encrypt_with_sm4(self.broker_encrypt_key, cjson.dumps(body))
149
157
  except Exception:
150
- return sm_tools.encrypt_with_sm4(self.broker_encrypt_key, body)
158
+ return sm_util.encrypt_with_sm4(self.broker_encrypt_key, body)
151
159
 
152
160
  def decrypt_body(self, body):
153
161
  if self.broker_encrypt_key:
154
162
  try:
155
- return cjson.loads(sm_tools.decrypt_with_sm4(self.broker_encrypt_key, body))
163
+ return cjson.loads(sm_util.decrypt_with_sm4(self.broker_encrypt_key, body))
156
164
  except Exception:
157
- sm_tools.decrypt_with_sm4(self.broker_encrypt_key, body)
165
+ sm_util.decrypt_with_sm4(self.broker_encrypt_key, body)
158
166
 
159
167
  def connect(self, username=None, password=None, timeout=10):
160
168
  if username and password:
ctools/sys_info.py CHANGED
@@ -1,13 +1,9 @@
1
1
  import hashlib
2
2
  import os
3
3
  import platform
4
- import socket
5
- import uuid
6
4
 
7
- import psutil
8
- from bottle import request
9
- import getpass
10
- from ctools import cjson, sm_tools, work_path
5
+ from ctools import cjson, path_info
6
+ from ctools.cipher import sm_util
11
7
 
12
8
  MACHINE_KEY = b'EnrGffoorbFyTYoS0902YyT1Fhehj4InpbezIDUuPOg='
13
9
 
@@ -15,12 +11,15 @@ MACHINE_KEY = b'EnrGffoorbFyTYoS0902YyT1Fhehj4InpbezIDUuPOg='
15
11
  class MachineInfo:
16
12
  machine_code = None
17
13
 
14
+
18
15
  def get_user():
16
+ import getpass
19
17
  return getpass.getuser()
20
18
 
19
+
21
20
  def get_machine_code():
22
21
  if MachineInfo.machine_code: return MachineInfo.machine_code
23
- destPath = os.path.join(work_path.get_user_work_path(), "AppData/Local/machine")
22
+ destPath = os.path.join(path_info.get_user_work_path(), "AppData/Local/machine")
24
23
  machine_file = os.path.join(destPath, 'machine_code.mc')
25
24
  origin_machine_code = get_origin_machine_code()
26
25
  if os.path.exists(machine_file):
@@ -28,7 +27,7 @@ def get_machine_code():
28
27
  file_code = f.readline()
29
28
  dec_code = ''
30
29
  try:
31
- dec_code = cjson.loads(sm_tools.decrypt_with_sm4(MACHINE_KEY, file_code))
30
+ dec_code = cjson.loads(sm_util.decrypt_with_sm4(MACHINE_KEY, file_code))
32
31
  origin_code = dec_code.get("origin_code")
33
32
  if origin_code == origin_machine_code:
34
33
  MachineInfo.machine_code = dec_code.get("hash_code")
@@ -38,7 +37,7 @@ def get_machine_code():
38
37
  print('machine code file is error: {} {}'.format(file_code, dec_code))
39
38
  hash_machine_code = get_hash_machine_code(origin_machine_code)
40
39
  machine_code = {"origin_code": origin_machine_code, "hash_code": hash_machine_code}
41
- enc_code = sm_tools.encrypt_with_sm4(MACHINE_KEY, cjson.dumps(machine_code))
40
+ enc_code = sm_util.encrypt_with_sm4(MACHINE_KEY, cjson.dumps(machine_code))
42
41
  os.makedirs(destPath, exist_ok=True)
43
42
  with open(machine_file, 'w') as f:
44
43
  f.write(enc_code)
@@ -68,26 +67,49 @@ def get_origin_machine_code():
68
67
  0
69
68
  )
70
69
  disk_serial = str(volume_serial.value)
71
- combined_info = cpu_serial + disk_serial + '-hylink'
70
+ combined_info = cpu_serial + disk_serial + '-gomyck'
72
71
  return hashlib.md5(combined_info.encode()).hexdigest()
73
72
 
74
73
 
75
74
  def get_hash_machine_code(origin_code):
75
+ import uuid
76
76
  code = origin_code + uuid.uuid1().hex
77
77
  machine_code = hashlib.md5(code.encode()).hexdigest()
78
78
  return machine_code.upper()
79
79
 
80
80
 
81
+ def get_public_ip():
82
+ import requests
83
+ try:
84
+ response = requests.get("https://api.ipify.org?format=json")
85
+ ip = response.json()["ip"]
86
+ return ip
87
+ except Exception as e:
88
+ return f"Failed to get public IP: {e}"
89
+
90
+
81
91
  def get_local_ipv4():
82
- ip = socket.gethostname()
92
+ import psutil
93
+ import socket
94
+ interfaces = psutil.net_if_addrs()
95
+ for interface, addresses in interfaces.items():
96
+ for address in addresses:
97
+ if address.family == socket.AF_INET and not address.address.startswith("127."):
98
+ return address.address
99
+ print("Failed to get local IPv4 address, try another way...")
100
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
83
101
  try:
84
- ip = socket.gethostbyname_ex(ip)[2][-1]
102
+ s.connect(("8.8.8.8", 80))
103
+ ip = s.getsockname()[0]
85
104
  except Exception:
86
- pass
105
+ ip = '127.0.0.1'
106
+ finally:
107
+ s.close()
87
108
  return ip
88
109
 
89
110
 
90
111
  def get_remote_ipv4():
112
+ from bottle import request
91
113
  try:
92
114
  return request.remote_route[0]
93
115
  except:
@@ -95,6 +117,7 @@ def get_remote_ipv4():
95
117
 
96
118
 
97
119
  def get_proc_pid_by(cmdline):
120
+ import psutil
98
121
  """
99
122
  根据命令行信息获取进程pid
100
123
  :param cmdline: