gomyck-tools 1.3.1__py3-none-any.whl → 1.3.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 +0 -0
- ctools/aes_tools.py +35 -0
- ctools/api_result.py +55 -0
- ctools/application.py +386 -0
- ctools/b64.py +7 -0
- ctools/bashPath.py +13 -0
- ctools/bottle_web_base.py +169 -0
- ctools/bottle_webserver.py +143 -0
- ctools/bottle_websocket.py +75 -0
- ctools/browser_element_tools.py +314 -0
- ctools/call.py +71 -0
- ctools/cftp.py +74 -0
- ctools/cjson.py +54 -0
- ctools/ckafka.py +159 -0
- ctools/compile_tools.py +18 -0
- ctools/console.py +55 -0
- ctools/coord_trans.py +127 -0
- ctools/credis.py +111 -0
- ctools/cron_lite.py +252 -0
- ctools/ctoken.py +34 -0
- ctools/cword.py +30 -0
- ctools/czip.py +130 -0
- ctools/database.py +185 -0
- ctools/date_utils.py +43 -0
- ctools/dict_wrapper.py +20 -0
- ctools/douglas_rarefy.py +136 -0
- ctools/download_tools.py +57 -0
- ctools/enums.py +4 -0
- ctools/ex.py +31 -0
- ctools/excelOpt.py +36 -0
- ctools/html_soup.py +35 -0
- ctools/http_utils.py +24 -0
- ctools/images_tools.py +27 -0
- ctools/imgDialog.py +44 -0
- ctools/metrics.py +131 -0
- ctools/mqtt_utils.py +289 -0
- ctools/obj.py +20 -0
- ctools/pacth.py +74 -0
- ctools/plan_area_tools.py +97 -0
- ctools/process_pool.py +36 -0
- ctools/pty_tools.py +72 -0
- ctools/resource_bundle_tools.py +121 -0
- ctools/rsa.py +70 -0
- ctools/screenshot_tools.py +127 -0
- ctools/sign.py +20 -0
- ctools/sm_tools.py +49 -0
- ctools/snow_id.py +76 -0
- ctools/str_diff.py +20 -0
- ctools/string_tools.py +85 -0
- ctools/sys_info.py +157 -0
- ctools/sys_log.py +89 -0
- ctools/thread_pool.py +35 -0
- ctools/upload_tools.py +40 -0
- ctools/win_canvas.py +83 -0
- ctools/win_control.py +106 -0
- ctools/word_fill.py +562 -0
- ctools/word_fill_entity.py +46 -0
- ctools/work_path.py +69 -0
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
- gomyck_tools-1.3.2.dist-info/RECORD +62 -0
- gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
- gomyck_tools-1.3.1.dist-info/RECORD +0 -4
- gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
import hashlib
|
2
|
+
import io
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
import shutil
|
6
|
+
import zipfile
|
7
|
+
from base64 import b64decode
|
8
|
+
from base64 import b64encode
|
9
|
+
|
10
|
+
from Crypto.Hash import MD5
|
11
|
+
from Crypto.PublicKey import RSA
|
12
|
+
from Crypto.Signature import pkcs1_15
|
13
|
+
|
14
|
+
from ctools import sys_log, work_path, application
|
15
|
+
|
16
|
+
log = sys_log.flog
|
17
|
+
|
18
|
+
|
19
|
+
def data_layout(manifest: dict):
|
20
|
+
"""
|
21
|
+
数据重新排版
|
22
|
+
:param manifest:
|
23
|
+
:return:
|
24
|
+
"""
|
25
|
+
data = ""
|
26
|
+
for key in sorted(manifest):
|
27
|
+
if key != "signature":
|
28
|
+
data += "%s=%s," % (key, manifest[key])
|
29
|
+
return data[:-1]
|
30
|
+
|
31
|
+
|
32
|
+
def get_md5_sum(data):
|
33
|
+
return hashlib.md5(data + "gomyck-daning".encode()).hexdigest()
|
34
|
+
|
35
|
+
|
36
|
+
def signature(manifest: dict):
|
37
|
+
"""
|
38
|
+
定义签名函数,能够使用指定的私钥对数据文件进行签名,并将签名结果输出到文件返回
|
39
|
+
:param manifest: 元数据字典
|
40
|
+
:return:
|
41
|
+
"""
|
42
|
+
with open(work_path.get_app_path() + '/keys/resource_private_key.pem', 'r') as pri:
|
43
|
+
private_key = RSA.import_key(pri.read())
|
44
|
+
digest = MD5.new(data_layout(manifest).encode('utf-8'))
|
45
|
+
return b64encode(pkcs1_15.new(private_key).sign(digest)).decode()
|
46
|
+
|
47
|
+
|
48
|
+
def sign_verify(manifest: dict, sign_val: str):
|
49
|
+
"""
|
50
|
+
定义签名验证函数,能够使用指定的公钥对任务2中的签名文件进行验证,返回验证结果
|
51
|
+
:param manifest: 元数据字典
|
52
|
+
:param sign_val: 对比的签名值
|
53
|
+
:return:
|
54
|
+
"""
|
55
|
+
with open(work_path.get_app_path() + '/keys/resource_public_key.pem', 'r') as pub:
|
56
|
+
public_key = RSA.import_key(pub.read())
|
57
|
+
digest = MD5.new(data_layout(manifest).encode('utf-8'))
|
58
|
+
try:
|
59
|
+
pkcs1_15.new(public_key).verify(digest, b64decode(sign_val))
|
60
|
+
return True
|
61
|
+
except:
|
62
|
+
return False
|
63
|
+
|
64
|
+
|
65
|
+
def unpack_resource(resource_name, file):
|
66
|
+
"""
|
67
|
+
解压资源包并验证包的完整性和签名
|
68
|
+
:param resource_name:
|
69
|
+
:param file:
|
70
|
+
:return:
|
71
|
+
"""
|
72
|
+
message = None
|
73
|
+
file_md5_sum = os.path.splitext(resource_name)[0].split('-')[-1]
|
74
|
+
if file_md5_sum != get_md5_sum(file.read()):
|
75
|
+
message = "资源包已损坏, 请重新打包"
|
76
|
+
else:
|
77
|
+
manifest = {}
|
78
|
+
with zipfile.ZipFile(file, 'r') as zf:
|
79
|
+
for file in zf.filelist:
|
80
|
+
if file.filename == "manifest.json":
|
81
|
+
manifest = json.loads(zf.read(file.filename))
|
82
|
+
break
|
83
|
+
# 校验签名
|
84
|
+
if sign_verify(manifest, manifest.get('signature')):
|
85
|
+
resource_path = os.path.join(application.Upload.source_pkg_path, manifest['name'])
|
86
|
+
shutil.rmtree(resource_path, ignore_errors=True)
|
87
|
+
zf.extractall(resource_path)
|
88
|
+
else:
|
89
|
+
message = "资源包签名校验未通过"
|
90
|
+
return message
|
91
|
+
|
92
|
+
|
93
|
+
def pack_resource(resource_path):
|
94
|
+
"""
|
95
|
+
压缩打包资源包为zip文件
|
96
|
+
:param resource_path:
|
97
|
+
:return:
|
98
|
+
"""
|
99
|
+
file_name = None
|
100
|
+
buffer = io.BytesIO()
|
101
|
+
manifest_path = os.path.join(resource_path, 'manifest.json')
|
102
|
+
if os.path.exists(manifest_path):
|
103
|
+
resource_name = os.path.split(resource_path)[-1]
|
104
|
+
with open(manifest_path, mode="r", encoding='utf-8') as f:
|
105
|
+
manifest = json.loads(f.read())
|
106
|
+
manifest["signature"] = signature(manifest)
|
107
|
+
with open(manifest_path, mode="w", encoding='utf-8') as f:
|
108
|
+
f.write(json.dumps(manifest, indent=2, ensure_ascii=False))
|
109
|
+
|
110
|
+
with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED, allowZip64=False) as zf:
|
111
|
+
for dir_path, dir_names, file_names in os.walk(resource_path):
|
112
|
+
for filename in file_names:
|
113
|
+
filepath = os.path.join(dir_path, filename)
|
114
|
+
arcname = os.path.join(dir_path.split(resource_name)[-1], filename)
|
115
|
+
zf.write(filepath, arcname=arcname)
|
116
|
+
buffer.seek(0)
|
117
|
+
md5_sum = get_md5_sum(buffer.getvalue())
|
118
|
+
file_name = "%s-%s.zip" % (resource_name, md5_sum)
|
119
|
+
else:
|
120
|
+
log.info("资源包%s不存在manifest.json元数据文件, 无法打包" % resource_path)
|
121
|
+
return file_name, buffer
|
ctools/rsa.py
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
import base64
|
2
|
+
|
3
|
+
from Crypto.Cipher import PKCS1_OAEP
|
4
|
+
from Crypto.PublicKey import RSA
|
5
|
+
from Crypto.Signature import pkcs1_15
|
6
|
+
from Crypto.Hash import SHA256
|
7
|
+
|
8
|
+
from ctools import work_path, cjson
|
9
|
+
|
10
|
+
ENCRYPT_CHUNK_SIZE = 245
|
11
|
+
decrypt_CHUNK_SIZE = 512
|
12
|
+
|
13
|
+
def generate_rsa_keypair(bits=2048):
|
14
|
+
key = RSA.generate(bits)
|
15
|
+
private_key = key.export_key() # 导出私钥
|
16
|
+
public_key = key.publickey().export_key() # 导出公钥
|
17
|
+
with open("private_key.pem", "wb") as f:
|
18
|
+
f.write(private_key)
|
19
|
+
with open("public_key.pem", "wb") as f:
|
20
|
+
f.write(public_key)
|
21
|
+
|
22
|
+
def loadLicenseInfo(auth_code):
|
23
|
+
with open(work_path.get_app_path() + '/keys/license.key', 'r') as pri:
|
24
|
+
decrypt_message = decrypt(auth_code.strip(), pri.read())
|
25
|
+
return cjson.loads(decrypt_message)
|
26
|
+
|
27
|
+
# 加密函数
|
28
|
+
def encrypt(msg, public_key):
|
29
|
+
parts = b''
|
30
|
+
public_key = RSA.import_key(public_key)
|
31
|
+
cipher = PKCS1_OAEP.new(public_key)
|
32
|
+
for i in range(0, len(msg), ENCRYPT_CHUNK_SIZE):
|
33
|
+
parts += cipher.encrypt(msg[i:i + ENCRYPT_CHUNK_SIZE].encode())
|
34
|
+
encrypted_base64 = base64.b64encode(parts)
|
35
|
+
return encrypted_base64.decode()
|
36
|
+
|
37
|
+
# 解密函数
|
38
|
+
def decrypt(msg, private_key):
|
39
|
+
parts = b''
|
40
|
+
public_key = RSA.import_key(private_key)
|
41
|
+
cipher = PKCS1_OAEP.new(public_key)
|
42
|
+
encrypted_bytes = base64.b64decode(msg)
|
43
|
+
for i in range(0, len(encrypted_bytes), decrypt_CHUNK_SIZE):
|
44
|
+
parts += cipher.decrypt(encrypted_bytes[i:i + decrypt_CHUNK_SIZE])
|
45
|
+
return parts.decode()
|
46
|
+
|
47
|
+
# 验签
|
48
|
+
def verify_sign(msg, public_key, sign):
|
49
|
+
public_key = RSA.import_key(public_key)
|
50
|
+
hash_message = SHA256.new(msg.encode())
|
51
|
+
try:
|
52
|
+
pkcs1_15.new(public_key).verify(hash_message, base64.b64decode(sign.encode()))
|
53
|
+
return True
|
54
|
+
except Exception as e:
|
55
|
+
print('签名验证失败: {}'.format(e))
|
56
|
+
return False
|
57
|
+
|
58
|
+
# 签名
|
59
|
+
def sign_msg(msg, private_key):
|
60
|
+
private_key = RSA.import_key(private_key)
|
61
|
+
hash_message = SHA256.new(msg.encode())
|
62
|
+
signature = pkcs1_15.new(private_key).sign(hash_message)
|
63
|
+
return base64.b64encode(signature).decode()
|
64
|
+
|
65
|
+
|
66
|
+
# with open(work_path.get_current_path() + '/private_key.pem', 'r') as key:
|
67
|
+
# key = key.read()
|
68
|
+
# sign = sign_msg(key, key)
|
69
|
+
# with open(work_path.get_current_path() + '/public_key.pem', 'r') as pub:
|
70
|
+
# print(verify_sign(key, pub.read(), sign+'123'))
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
import tkinter as tk
|
4
|
+
|
5
|
+
import pyautogui
|
6
|
+
|
7
|
+
from ctools import application, string_tools
|
8
|
+
|
9
|
+
"""
|
10
|
+
截屏工具类, 按回车截图
|
11
|
+
image_path=screenshot_tools.screenshot()
|
12
|
+
"""
|
13
|
+
|
14
|
+
|
15
|
+
class ScreenshotTools:
|
16
|
+
def __init__(self):
|
17
|
+
self.root = tk.Tk()
|
18
|
+
self.root.overrideredirect(True)
|
19
|
+
self.root.attributes("-alpha", 0.1)
|
20
|
+
self.root.attributes('-topmost', 'true')
|
21
|
+
self.root.geometry("{0}x{1}+0+0".format(self.root.winfo_screenwidth(), self.root.winfo_screenheight()))
|
22
|
+
self.root.configure(bg="black")
|
23
|
+
|
24
|
+
self.canvas = tk.Canvas(self.root)
|
25
|
+
self.canvas.bind("<Button-3>", self.change_center_point)
|
26
|
+
|
27
|
+
self.x, self.y = 0, 0
|
28
|
+
self.xstart, self.ystart = 0, 0
|
29
|
+
self.xend, self.yend = 0, 0
|
30
|
+
self.xcenter, self.ycenter = 0, 0
|
31
|
+
|
32
|
+
self.select_area = self.canvas.create_rectangle(0, 0, 0, 0, outline='red', width=0, dash=(4, 4))
|
33
|
+
self.screenshot_path = None
|
34
|
+
self.center_offset = (0, 0)
|
35
|
+
|
36
|
+
self.pos_div = tk.Toplevel(self.root)
|
37
|
+
self.pos_div.overrideredirect(True)
|
38
|
+
self.pos_div.attributes('-topmost', 'true')
|
39
|
+
self.pos_div.configure(bg="grey")
|
40
|
+
self.pos_div.geometry("360x25")
|
41
|
+
|
42
|
+
self.pos_div_text = "POS: (%s, %s), Enter键-截图, Esc键-退出, 右键设置点击位置"
|
43
|
+
self.pos_label = tk.Label(self.pos_div, text=self.pos_div_text % (0, 0), bg="black", fg="white")
|
44
|
+
self.pos_label.pack(fill="both", expand=True)
|
45
|
+
|
46
|
+
self.center_div = None
|
47
|
+
|
48
|
+
self.root.bind('<Escape>', self.sys_out) # 键盘Esc键->退出
|
49
|
+
self.root.bind('<Motion>', self.print_pos)
|
50
|
+
self.root.bind("<Button-1>", self.mouse_left_down) # 鼠标左键点击->显示子窗口
|
51
|
+
self.root.bind("<B1-Motion>", self.mouse_left_move) # 鼠标左键移动->改变子窗口大小
|
52
|
+
self.root.bind("<ButtonRelease-1>", self.mouse_left_up) # 鼠标左键释放->记录最后光标的位置
|
53
|
+
self.root.bind("<Return>", self.save_image) # 回车键->截屏并保存图片
|
54
|
+
self.root.focus()
|
55
|
+
|
56
|
+
def start(self):
|
57
|
+
self.root.mainloop()
|
58
|
+
|
59
|
+
def print_pos(self, event):
|
60
|
+
self.x, self.y = event.widget.winfo_pointerxy()
|
61
|
+
self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
|
62
|
+
self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
|
63
|
+
|
64
|
+
def change_center_point(self, event):
|
65
|
+
offset_x = event.x + self.xstart - self.xcenter - 2
|
66
|
+
offset_y = event.y + self.ystart - self.ycenter - 2
|
67
|
+
self.center_div.geometry(f"+{self.xstart+event.x-2}+{self.ystart+event.y-2}")
|
68
|
+
self.center_offset = (offset_x, offset_y)
|
69
|
+
|
70
|
+
def mouse_left_down(self, event):
|
71
|
+
if self.center_div is None:
|
72
|
+
self.center_div = tk.Toplevel(self.root)
|
73
|
+
self.center_div.overrideredirect(True)
|
74
|
+
self.center_div.attributes('-topmost', 'true')
|
75
|
+
self.center_div.configure(bg="red")
|
76
|
+
self.center_div.geometry("4x4")
|
77
|
+
|
78
|
+
self.x, self.y = event.x, event.y
|
79
|
+
self.xstart, self.ystart = event.x, event.y
|
80
|
+
self.canvas.configure(height=1)
|
81
|
+
self.canvas.configure(width=1)
|
82
|
+
self.canvas.config(highlightthickness=0) # 无边框
|
83
|
+
self.canvas.place(x=event.x, y=event.y)
|
84
|
+
self.center_div.geometry(f"+{self.xstart}+{self.ystart}")
|
85
|
+
|
86
|
+
def mouse_left_move(self, event):
|
87
|
+
self.x, self.y = event.x, event.y
|
88
|
+
self.xcenter = self.xstart + int((self.x-self.xstart)/2)-2
|
89
|
+
self.ycenter = self.ystart + int((self.y-self.ystart)/2)-2
|
90
|
+
|
91
|
+
self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
|
92
|
+
self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
|
93
|
+
self.center_div.geometry(f"+{self.xcenter}+{self.ycenter}")
|
94
|
+
|
95
|
+
self.canvas.configure(height=event.y - self.ystart)
|
96
|
+
self.canvas.configure(width=event.x - self.xstart)
|
97
|
+
self.canvas.coords(self.select_area, 0, 0, event.x - self.xstart, event.y - self.ystart)
|
98
|
+
|
99
|
+
def mouse_left_up(self, event):
|
100
|
+
self.xend, self.yend = event.x, event.y
|
101
|
+
|
102
|
+
def save_image(self, event):
|
103
|
+
try:
|
104
|
+
self.canvas.delete(self.select_area)
|
105
|
+
self.canvas.place_forget()
|
106
|
+
self.sys_out()
|
107
|
+
|
108
|
+
time.sleep(0.3)
|
109
|
+
img = pyautogui.screenshot(region=[self.xstart, self.ystart, self.xend - self.xstart, self.yend - self.ystart])
|
110
|
+
self.screenshot_path = os.path.join(application.Server.screenshotPath,
|
111
|
+
"screenshot-%s.png" % string_tools.get_snowflake_id())
|
112
|
+
img.save(self.screenshot_path)
|
113
|
+
except Exception:
|
114
|
+
pass
|
115
|
+
|
116
|
+
def sys_out(self, event=None):
|
117
|
+
if self.center_div:
|
118
|
+
self.center_div.destroy()
|
119
|
+
self.pos_div.destroy()
|
120
|
+
self.root.destroy()
|
121
|
+
|
122
|
+
|
123
|
+
def screenshot():
|
124
|
+
st = ScreenshotTools()
|
125
|
+
st.start()
|
126
|
+
return st.screenshot_path, st.center_offset
|
127
|
+
|
ctools/sign.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import hashlib
|
2
|
+
import hmac
|
3
|
+
|
4
|
+
global_key = 'gomyck2014'
|
5
|
+
|
6
|
+
|
7
|
+
def generate_signature(file_path, key: str = global_key):
|
8
|
+
try:
|
9
|
+
with open(file_path, 'rb') as f:
|
10
|
+
file_contents = f.read()
|
11
|
+
file_hash = hashlib.sha256(file_contents).digest()
|
12
|
+
sign_val = hmac.new(key.encode(), file_hash, hashlib.sha256).digest()
|
13
|
+
return sign_val.hex()
|
14
|
+
except:
|
15
|
+
return ''
|
16
|
+
|
17
|
+
def digest(value: str, key: str = global_key):
|
18
|
+
val_hash = hashlib.sha256(value.encode()).digest()
|
19
|
+
sign_val = hmac.new(key.encode(), val_hash, hashlib.sha256).digest()
|
20
|
+
return sign_val.hex()
|
ctools/sm_tools.py
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import base64
|
2
|
+
|
3
|
+
from gmssl import sm2
|
4
|
+
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
|
5
|
+
|
6
|
+
sm2_crypt: sm2.CryptSM2 = None
|
7
|
+
|
8
|
+
def init(private_key: str, public_key: str):
|
9
|
+
global sm2_crypt
|
10
|
+
if sm2_crypt is not None:
|
11
|
+
print('sm2 is already init!!!')
|
12
|
+
return
|
13
|
+
sm2_crypt = sm2.CryptSM2(private_key=private_key, public_key=public_key, asn1=True, mode=1)
|
14
|
+
|
15
|
+
def sign_with_sm2(sign_data: str) -> str:
|
16
|
+
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
17
|
+
return sm2_crypt.sign_with_sm3(sign_data.encode('UTF-8'))
|
18
|
+
|
19
|
+
|
20
|
+
def verify_with_sm2(sign_val: str, sign_data: str) -> bool:
|
21
|
+
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
22
|
+
try:
|
23
|
+
return sm2_crypt.verify_with_sm3(sign_val, sign_data.encode('UTF-8'))
|
24
|
+
except Exception as e:
|
25
|
+
print('签名验证失败: {}'.format(e))
|
26
|
+
return False
|
27
|
+
|
28
|
+
def encrypt_with_sm2(encrypt_data: str) -> str:
|
29
|
+
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
30
|
+
return base64.b64encode(sm2_crypt.encrypt(encrypt_data.encode('UTF-8'))).decode('UTF-8')
|
31
|
+
|
32
|
+
|
33
|
+
def decrypt_with_sm2(encrypt_data: str) -> str:
|
34
|
+
if sm2_crypt is None: raise Exception('sm2 is not init!!!')
|
35
|
+
return sm2_crypt.decrypt(base64.b64decode(encrypt_data.encode('UTF-8'))).decode('UTF-8')
|
36
|
+
|
37
|
+
|
38
|
+
def encrypt_with_sm4(key: bytes, encrypt_text: str):
|
39
|
+
crypt_sm4 = CryptSM4()
|
40
|
+
crypt_sm4.set_key(key, SM4_ENCRYPT)
|
41
|
+
encrypt_value = base64.b64encode(crypt_sm4.crypt_ecb(encrypt_text.encode()))
|
42
|
+
return encrypt_value.decode()
|
43
|
+
|
44
|
+
|
45
|
+
def decrypt_with_sm4(key: bytes, decrypt_text: str):
|
46
|
+
crypt_sm4 = CryptSM4()
|
47
|
+
crypt_sm4.set_key(key, SM4_DECRYPT)
|
48
|
+
decrypt_value = crypt_sm4.crypt_ecb(base64.b64decode(decrypt_text))
|
49
|
+
return decrypt_value.decode()
|
ctools/snow_id.py
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
# 64位ID的划分
|
4
|
+
WORKER_ID_BITS = 5
|
5
|
+
DATACENTER_ID_BITS = 5
|
6
|
+
SEQUENCE_BITS = 12
|
7
|
+
# 最大取值计算
|
8
|
+
MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS) # 2**5-1 0b11111
|
9
|
+
MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)
|
10
|
+
# 移位偏移计算
|
11
|
+
WOKER_ID_SHIFT = SEQUENCE_BITS
|
12
|
+
DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
|
13
|
+
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS
|
14
|
+
# 序号循环掩码
|
15
|
+
SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS)
|
16
|
+
# Twitter元年时间戳
|
17
|
+
TWEPOCH = 1288834974657
|
18
|
+
|
19
|
+
class SnowId(object):
|
20
|
+
"""
|
21
|
+
用于生成IDs
|
22
|
+
"""
|
23
|
+
def __init__(self, datacenter_id=0, worker_id=0, sequence=0):
|
24
|
+
"""
|
25
|
+
初始化
|
26
|
+
:param datacenter_id: 数据中心(机器区域)ID
|
27
|
+
:param worker_id: 机器ID
|
28
|
+
:param sequence: 起始序号
|
29
|
+
"""
|
30
|
+
# sanity check
|
31
|
+
if worker_id > MAX_WORKER_ID or worker_id < 0:
|
32
|
+
raise ValueError('worker_id值越界')
|
33
|
+
if datacenter_id > MAX_DATACENTER_ID or datacenter_id < 0:
|
34
|
+
raise ValueError('datacenter_id值越界')
|
35
|
+
self.worker_id = worker_id
|
36
|
+
self.datacenter_id = datacenter_id
|
37
|
+
self.sequence = sequence
|
38
|
+
self.last_timestamp = -1 # 上次计算的时间戳
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def _gen_timestamp():
|
42
|
+
"""
|
43
|
+
生成整数时间戳
|
44
|
+
:return:int timestamp
|
45
|
+
"""
|
46
|
+
return int(time.time() * 1000)
|
47
|
+
|
48
|
+
def get_id(self):
|
49
|
+
"""
|
50
|
+
获取新ID
|
51
|
+
:return:
|
52
|
+
"""
|
53
|
+
timestamp = self._gen_timestamp()
|
54
|
+
# 时钟回拨
|
55
|
+
if timestamp < self.last_timestamp:
|
56
|
+
print('clock is moving backwards. Rejecting requests until {}'.format(self.last_timestamp))
|
57
|
+
raise
|
58
|
+
if timestamp == self.last_timestamp:
|
59
|
+
self.sequence = (self.sequence + 1) & SEQUENCE_MASK
|
60
|
+
if self.sequence == 0:
|
61
|
+
timestamp = self._til_next_millis(self.last_timestamp)
|
62
|
+
else:
|
63
|
+
self.sequence = 0
|
64
|
+
self.last_timestamp = timestamp
|
65
|
+
new_id = ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (self.datacenter_id << DATACENTER_ID_SHIFT) | \
|
66
|
+
(self.worker_id << WOKER_ID_SHIFT) | self.sequence
|
67
|
+
return new_id
|
68
|
+
|
69
|
+
def _til_next_millis(self, last_timestamp):
|
70
|
+
"""
|
71
|
+
等到下一毫秒
|
72
|
+
"""
|
73
|
+
timestamp = self._gen_timestamp()
|
74
|
+
while timestamp <= last_timestamp:
|
75
|
+
timestamp = self._gen_timestamp()
|
76
|
+
return timestamp
|
ctools/str_diff.py
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# import difflib
|
2
|
+
#
|
3
|
+
# s1 = '12-23'
|
4
|
+
# s2 = '00-11'
|
5
|
+
# s3 = '2023-05-05 12:00:00-2023-05-06 23:00:00 剩余4'
|
6
|
+
#
|
7
|
+
# sm = difflib.SequenceMatcher(None, s1, s3)
|
8
|
+
#
|
9
|
+
# print(sm.ratio()) # 输出相似度得分
|
10
|
+
|
11
|
+
|
12
|
+
from fuzzywuzzy import fuzz
|
13
|
+
|
14
|
+
s1 = '12-23'
|
15
|
+
s2 = '00-11'
|
16
|
+
s3 = '2023-05-05 12:00:00-2023-05-06 23:00:00 剩余4'
|
17
|
+
s4 = '2023-04-28 00:00:00-2023-04-28 11:00:00 剩余12'
|
18
|
+
score = fuzz.ratio(s2, s4)
|
19
|
+
|
20
|
+
print(score) # 输出相似度得
|
ctools/string_tools.py
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
from ctools.snow_id import SnowId
|
2
|
+
|
3
|
+
idWorker = SnowId(1, 2, 0)
|
4
|
+
|
5
|
+
def get_random_str(size: int = 10) -> str:
|
6
|
+
import random
|
7
|
+
return "".join(random.sample('abcdefghjklmnpqrstuvwxyz123456789', size))
|
8
|
+
|
9
|
+
def get_uuid() -> str:
|
10
|
+
import uuid
|
11
|
+
return str(uuid.uuid1()).replace("-", "")
|
12
|
+
|
13
|
+
def get_snowflake_id():
|
14
|
+
return idWorker.get_id()
|
15
|
+
|
16
|
+
def decode_bytes(bytes_str):
|
17
|
+
import chardet
|
18
|
+
res_str = ""
|
19
|
+
if bytes_str:
|
20
|
+
detect = chardet.detect(bytes_str)
|
21
|
+
if detect:
|
22
|
+
confidence = 0
|
23
|
+
chardet_error = False
|
24
|
+
try:
|
25
|
+
confidence = float(detect.get('confidence'))
|
26
|
+
res_str = bytes_str.decode(encoding=detect.get('encoding'))
|
27
|
+
except Exception:
|
28
|
+
chardet_error = True
|
29
|
+
|
30
|
+
try:
|
31
|
+
if confidence <= 0.95 or chardet_error:
|
32
|
+
encoding = "utf-8" if detect.get('encoding') == "utf-8" else "gbk"
|
33
|
+
res_str = bytes_str.decode(encoding=encoding)
|
34
|
+
except Exception:
|
35
|
+
res_str = str(bytes_str)
|
36
|
+
return res_str
|
37
|
+
|
38
|
+
|
39
|
+
def check_sum(content: str):
|
40
|
+
import hashlib
|
41
|
+
try:
|
42
|
+
algorithm = hashlib.sha256()
|
43
|
+
algorithm.update(content.encode())
|
44
|
+
return algorithm.hexdigest()
|
45
|
+
except Exception:
|
46
|
+
return None
|
47
|
+
|
48
|
+
|
49
|
+
def is_list(v: str):
|
50
|
+
try:
|
51
|
+
list(v)
|
52
|
+
if v[0] == "[" and v[-1] == "]":
|
53
|
+
return True
|
54
|
+
else:
|
55
|
+
return False
|
56
|
+
except Exception:
|
57
|
+
return False
|
58
|
+
|
59
|
+
def is_digit(v: str):
|
60
|
+
try:
|
61
|
+
float(v)
|
62
|
+
return True
|
63
|
+
except Exception:
|
64
|
+
return False
|
65
|
+
|
66
|
+
def is_bool(v: str):
|
67
|
+
if v in ["False", "True"]:
|
68
|
+
return True
|
69
|
+
else:
|
70
|
+
return False
|
71
|
+
|
72
|
+
def dict_to_params(obj: dict):
|
73
|
+
params = ""
|
74
|
+
for k, v in obj.items():
|
75
|
+
if k == 'varname':
|
76
|
+
continue
|
77
|
+
v = str(v)
|
78
|
+
if not is_list(v) and not is_digit(v) and not is_bool(v):
|
79
|
+
if k == "path" and v[:4] != "http":
|
80
|
+
v = "r'%s'" % v
|
81
|
+
else:
|
82
|
+
v = "'%s'" % v
|
83
|
+
params += "%s=%s, " % (k, v)
|
84
|
+
params = params[:params.rfind(',')]
|
85
|
+
return params
|