pyscriptbase 1.0.1__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.
- pybase/__init__.py +0 -0
- pybase/cipher.py +169 -0
- pybase/database.py +470 -0
- pybase/env.py +87 -0
- pybase/net.py +440 -0
- pybase/panel.py +194 -0
- pybase/pusher.py +194 -0
- pybase/script.py +90 -0
- pybase/util.py +86 -0
- pybase/webview.py +54 -0
- pyscriptbase-1.0.1.dist-info/METADATA +24 -0
- pyscriptbase-1.0.1.dist-info/RECORD +14 -0
- pyscriptbase-1.0.1.dist-info/WHEEL +5 -0
- pyscriptbase-1.0.1.dist-info/top_level.txt +1 -0
pybase/panel.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# encoding=utf8
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import warnings
|
|
5
|
+
from json import dumps as jsonDumps
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
warnings.filterwarnings("ignore")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Envrionment:
|
|
12
|
+
def __init__(self, id: int, name: str, value: str, remarks: str = "") -> None:
|
|
13
|
+
"""
|
|
14
|
+
初始化
|
|
15
|
+
"""
|
|
16
|
+
self.id = id
|
|
17
|
+
self.name = name
|
|
18
|
+
self.value = value
|
|
19
|
+
self.remarks = ""
|
|
20
|
+
|
|
21
|
+
def toJson(self) -> dict:
|
|
22
|
+
return {
|
|
23
|
+
"id": self.id,
|
|
24
|
+
"name": self.name,
|
|
25
|
+
"value": self.value,
|
|
26
|
+
"remarks": self.remarks,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PanelClient:
|
|
31
|
+
headers = {"content-type": "application/json", "accept": "application/json"}
|
|
32
|
+
|
|
33
|
+
def __init__(self, address: str, id: str, secret: str) -> None:
|
|
34
|
+
"""
|
|
35
|
+
初始化
|
|
36
|
+
"""
|
|
37
|
+
self.address = self.__validAddress__(address)
|
|
38
|
+
self.clientId = id
|
|
39
|
+
self.clientSecret = secret
|
|
40
|
+
self.valid = False
|
|
41
|
+
self.session = requests.session()
|
|
42
|
+
self.session.headers.update(self.headers)
|
|
43
|
+
|
|
44
|
+
self.__login__()
|
|
45
|
+
|
|
46
|
+
def __validAddress__(self, address: str) -> str:
|
|
47
|
+
if not address.startswith("http://") and not address.startswith("https://"):
|
|
48
|
+
address = f"http://{address}"
|
|
49
|
+
if address.endswith("/"):
|
|
50
|
+
address = address[0:-1]
|
|
51
|
+
return address
|
|
52
|
+
|
|
53
|
+
def __log__(self, content: str, start="", end="\n") -> None:
|
|
54
|
+
"""
|
|
55
|
+
日志
|
|
56
|
+
"""
|
|
57
|
+
print(content)
|
|
58
|
+
|
|
59
|
+
def __login__(self) -> None:
|
|
60
|
+
"""
|
|
61
|
+
登录
|
|
62
|
+
"""
|
|
63
|
+
url = f"{self.address}/open/auth/token?client_id={self.clientId}&client_secret={self.clientSecret}"
|
|
64
|
+
try:
|
|
65
|
+
rjson = self.session.get(url, verify=False).json()
|
|
66
|
+
if rjson["code"] == 200:
|
|
67
|
+
self.auth = f"{rjson['data']['token_type']} {rjson['data']['token']}"
|
|
68
|
+
self.session.headers.update({"Authorization": self.auth})
|
|
69
|
+
self.valid = True
|
|
70
|
+
self.__log__(f"面板登录成功:{rjson['data']['token']}", start="\n")
|
|
71
|
+
else:
|
|
72
|
+
self.valid = False
|
|
73
|
+
self.__log__(f"面板登录失败:{rjson['message']}", start="\n")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
self.valid = False
|
|
76
|
+
self.__log__(f"面板登录失败:{str(e)}", start="\n")
|
|
77
|
+
|
|
78
|
+
def addEnvs(self, envs: List[Envrionment]) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
新建环境变量
|
|
81
|
+
"""
|
|
82
|
+
url = f"{self.address}/open/envs"
|
|
83
|
+
try:
|
|
84
|
+
body = []
|
|
85
|
+
for env in envs:
|
|
86
|
+
body.append({"name": env.name, "value": env.value, "remarks": env.remarks})
|
|
87
|
+
rjson = self.session.post(url, data=jsonDumps(body), verify=False).json()
|
|
88
|
+
if rjson["code"] == 200:
|
|
89
|
+
self.__log__(f"新建环境变量成功:{len(envs)}")
|
|
90
|
+
return True
|
|
91
|
+
else:
|
|
92
|
+
self.__log__(f"新建环境变量失败:{rjson['message']}")
|
|
93
|
+
return False
|
|
94
|
+
except Exception as e:
|
|
95
|
+
self.__log__(f"新建环境变量失败:{str(e)}")
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
def updateEnvs(self, env: Envrionment) -> bool:
|
|
99
|
+
"""
|
|
100
|
+
更新环境变量
|
|
101
|
+
"""
|
|
102
|
+
url = f"{self.address}/open/envs"
|
|
103
|
+
try:
|
|
104
|
+
body = jsonDumps(env.toJson())
|
|
105
|
+
rjson = self.session.put(url, verify=False, data=body).json()
|
|
106
|
+
if rjson["code"] == 200:
|
|
107
|
+
self.__log__(f"更新环境变量成功")
|
|
108
|
+
else:
|
|
109
|
+
self.__log__(f"更新环境变量失败:{rjson['message']}")
|
|
110
|
+
except Exception as e:
|
|
111
|
+
self.__log__(f"更新环境变量失败:{str(e)}")
|
|
112
|
+
|
|
113
|
+
def addOrUpdateEnv(self, env: Envrionment) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
添加或更新环境变量
|
|
116
|
+
"""
|
|
117
|
+
envs = self.getEnvs()
|
|
118
|
+
if not envs:
|
|
119
|
+
self.addEnvs([env])
|
|
120
|
+
return True
|
|
121
|
+
for env_ in envs:
|
|
122
|
+
if env_.name == env.name:
|
|
123
|
+
env.id = env_.id
|
|
124
|
+
self.updateEnvs(env)
|
|
125
|
+
return True
|
|
126
|
+
self.addEnvs([env])
|
|
127
|
+
|
|
128
|
+
def getEnvs(self, search: str = "") -> list[Envrionment]:
|
|
129
|
+
"""
|
|
130
|
+
获取环境变量
|
|
131
|
+
"""
|
|
132
|
+
url = f"{self.address}/open/envs?searchValue={search}"
|
|
133
|
+
result: list[Envrionment] = []
|
|
134
|
+
try:
|
|
135
|
+
rjson = self.session.get(url, verify=False).json()
|
|
136
|
+
if rjson["code"] == 200:
|
|
137
|
+
for env in rjson["data"]:
|
|
138
|
+
result.append(Envrionment(env["id"], env["name"], env["value"], env["remarks"]))
|
|
139
|
+
else:
|
|
140
|
+
self.__log__(f"获取环境变量失败:{rjson['message']}")
|
|
141
|
+
except Exception as e:
|
|
142
|
+
self.__log__(f"获取环境变量失败:{str(e)}")
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
def deleteEnvs(self, ids: list[int]) -> bool:
|
|
146
|
+
"""
|
|
147
|
+
删除环境变量
|
|
148
|
+
"""
|
|
149
|
+
url = f"{self.address}/open/envs"
|
|
150
|
+
try:
|
|
151
|
+
rjson = self.session.delete(url, data=jsonDumps(ids), verify=False).json()
|
|
152
|
+
if rjson["code"] == 200:
|
|
153
|
+
self.__log__(f"删除环境变量成功:{len(ids)}")
|
|
154
|
+
return True
|
|
155
|
+
else:
|
|
156
|
+
self.__log__(f"删除环境变量失败:{rjson['message']}")
|
|
157
|
+
return False
|
|
158
|
+
except Exception as e:
|
|
159
|
+
self.__log__(f"删除环境变量失败:{str(e)}")
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
def enableEnvs(self, ids: list[int]) -> bool:
|
|
163
|
+
"""
|
|
164
|
+
启用环境变量
|
|
165
|
+
"""
|
|
166
|
+
url = f"{self.address}/open/envs/enable"
|
|
167
|
+
try:
|
|
168
|
+
rjson = self.session.put(url, data=jsonDumps(ids), verify=False).json()
|
|
169
|
+
if rjson["code"] == 200:
|
|
170
|
+
self.__log__(f"启用环境变量成功:{len(ids)}")
|
|
171
|
+
return True
|
|
172
|
+
else:
|
|
173
|
+
self.__log__(f"启用环境变量失败:{rjson['message']}")
|
|
174
|
+
return False
|
|
175
|
+
except Exception as e:
|
|
176
|
+
self.__log__(f"启用环境变量失败:{str(e)}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
def disableEnvs(self, ids: list[int]) -> bool:
|
|
180
|
+
"""
|
|
181
|
+
禁用环境变量
|
|
182
|
+
"""
|
|
183
|
+
url = f"{self.address}/open/envs/disable"
|
|
184
|
+
try:
|
|
185
|
+
rjson = self.session.put(url, data=jsonDumps(ids), verify=False).json()
|
|
186
|
+
if rjson["code"] == 200:
|
|
187
|
+
self.__log__(f"禁用环境变量成功:{len(ids)}")
|
|
188
|
+
return True
|
|
189
|
+
else:
|
|
190
|
+
self.__log__(f"禁用环境变量失败:{rjson['message']}")
|
|
191
|
+
return False
|
|
192
|
+
except Exception as e:
|
|
193
|
+
self.__log__(f"禁用环境变量失败:{str(e)}")
|
|
194
|
+
return False
|
pybase/pusher.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import smtplib
|
|
2
|
+
from email.mime.multipart import MIMEMultipart
|
|
3
|
+
from email.mime.text import MIMEText
|
|
4
|
+
from . import env as Env
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HtmlTableEmail:
|
|
8
|
+
"""
|
|
9
|
+
HTML表格对象
|
|
10
|
+
@headers=列数
|
|
11
|
+
@rows=函数 不足自动补充空字符串 多余则忽略 [str|int|tuple] tuple=(str|int,G|R)有颜色
|
|
12
|
+
@extras 末尾额外信息
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, title: str, headers: list[str] = [], appendIndex: bool = True):
|
|
16
|
+
self.title = title
|
|
17
|
+
self.headers: list[str] = headers
|
|
18
|
+
self.rows: list[str | int] = []
|
|
19
|
+
self.extras: list[str] = []
|
|
20
|
+
self.appendIndex = appendIndex
|
|
21
|
+
|
|
22
|
+
def toStr(self) -> str:
|
|
23
|
+
result = ""
|
|
24
|
+
for index, row in enumerate(self.rows):
|
|
25
|
+
result += f"[{index+1}] "
|
|
26
|
+
for i in range(len(row)):
|
|
27
|
+
if isinstance(row[i], tuple) and len(row[i]) >= 1:
|
|
28
|
+
result += f"{row[i][0]} "
|
|
29
|
+
else:
|
|
30
|
+
result += f"{row[i]} "
|
|
31
|
+
result += "\n"
|
|
32
|
+
for extra in self.extras:
|
|
33
|
+
result += f"\n{extra}\n"
|
|
34
|
+
return result
|
|
35
|
+
|
|
36
|
+
def toHtml(self) -> str:
|
|
37
|
+
table = self
|
|
38
|
+
thead = ""
|
|
39
|
+
tbody = ""
|
|
40
|
+
extras = ""
|
|
41
|
+
|
|
42
|
+
if table.appendIndex:
|
|
43
|
+
thead += "<th></th>"
|
|
44
|
+
for header in table.headers:
|
|
45
|
+
thead += f"<th>{header}</th>"
|
|
46
|
+
|
|
47
|
+
for index, row in enumerate(table.rows):
|
|
48
|
+
trs = ""
|
|
49
|
+
if table.appendIndex:
|
|
50
|
+
trs += f"<td>{index+1}</td>"
|
|
51
|
+
for i in range(len(table.headers)):
|
|
52
|
+
if len(row) - 1 >= i:
|
|
53
|
+
if isinstance(row[i], tuple) and len(row[i]) >= 2:
|
|
54
|
+
# 颜色
|
|
55
|
+
if str(row[i][1]).upper in ["G", "GREEN"]:
|
|
56
|
+
trs += f"<td class='green'>{row[i][0]}</td>"
|
|
57
|
+
else:
|
|
58
|
+
trs += f"<td class='red'>{row[i][0]}</td>"
|
|
59
|
+
else:
|
|
60
|
+
# 常规
|
|
61
|
+
trs += f"<td>{row[i]}</td>"
|
|
62
|
+
else:
|
|
63
|
+
trs += "<td></td>"
|
|
64
|
+
tbody += f"<tr>{trs}</tr>"
|
|
65
|
+
|
|
66
|
+
if table.extras:
|
|
67
|
+
extras = '<tr><th colspan="$$spans$$">其他报告</th></tr>'
|
|
68
|
+
for extra in table.extras:
|
|
69
|
+
extras += f'<tr><td colspan="$$spans$$">{extra}</td></tr>'
|
|
70
|
+
|
|
71
|
+
html = """
|
|
72
|
+
<!DOCTYPE html>
|
|
73
|
+
<html lang="zh-CN">
|
|
74
|
+
<head>
|
|
75
|
+
<meta charset="UTF-8" />
|
|
76
|
+
<title></title>
|
|
77
|
+
<style>
|
|
78
|
+
table {
|
|
79
|
+
width: 100%; /* 设置表格宽度 */
|
|
80
|
+
margin: auto; /* 表格居中 */
|
|
81
|
+
border-collapse: collapse; /* 边框合并 */
|
|
82
|
+
font-size: 14px;
|
|
83
|
+
text-align: center; /* 文本居中 */
|
|
84
|
+
}
|
|
85
|
+
th,
|
|
86
|
+
td {
|
|
87
|
+
padding: 10px; /* 内边距 */
|
|
88
|
+
border: 1px solid #ddd; /* 边框样式 */
|
|
89
|
+
}
|
|
90
|
+
th {
|
|
91
|
+
background-color: #f2f2f2; /* 表头背景颜色 */
|
|
92
|
+
}
|
|
93
|
+
tr:nth-child(even) {
|
|
94
|
+
background-color: #f9f9f9; /* 隔行变色 */
|
|
95
|
+
}
|
|
96
|
+
caption {
|
|
97
|
+
font-size: 1.5em;
|
|
98
|
+
margin-bottom: 10px;
|
|
99
|
+
}
|
|
100
|
+
.green {
|
|
101
|
+
color: green; /* 绿色文本 */
|
|
102
|
+
}
|
|
103
|
+
.red {
|
|
104
|
+
color: red; /* 红色文本 */
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
107
|
+
</head>
|
|
108
|
+
<body>
|
|
109
|
+
<table>
|
|
110
|
+
<thead><tr>$$thead$$</tr></thead>
|
|
111
|
+
<tbody>
|
|
112
|
+
$$tbody$$
|
|
113
|
+
$$extras$$
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
</body>
|
|
117
|
+
</html>
|
|
118
|
+
"""
|
|
119
|
+
spans = len(table.headers) + (1 if table.appendIndex else 0)
|
|
120
|
+
html = html.replace("$$thead$$", thead)
|
|
121
|
+
html = html.replace("$$tbody$$", tbody)
|
|
122
|
+
html = html.replace("$$extras$$", extras)
|
|
123
|
+
html = html.replace("$$spans$$", str(spans))
|
|
124
|
+
return html
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SMTPParams:
|
|
128
|
+
def __init__(self, server: str, port: int, sender: str, password: str):
|
|
129
|
+
self.server = server
|
|
130
|
+
self.port = port
|
|
131
|
+
self.sender = sender
|
|
132
|
+
self.password = password
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def sendQQMail(title: str, content: str, receiver: str = None, params: SMTPParams = None):
|
|
136
|
+
# 设置SMTP服务器地址和端口
|
|
137
|
+
smtp_server = params.server if params else Env.get("smtp_server")
|
|
138
|
+
port = params.port if params else Env.getInt("smtp_port") # 对于TLS,通常使用587端口;对于SSL,使用465端口
|
|
139
|
+
sender = params.sender if params else Env.get("smtp_sender") # 你的电子邮件地址
|
|
140
|
+
password = params.password if params else Env.get("smtp_password") # 你的电子邮件密码(对于某些电子邮件提供商,你可能需要生成一个应用专用密码)
|
|
141
|
+
receiver = receiver if receiver else Env.get("smtp_receiver")
|
|
142
|
+
|
|
143
|
+
if not sender or not password:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# 创建消息对象
|
|
147
|
+
message = MIMEMultipart()
|
|
148
|
+
message["From"] = sender
|
|
149
|
+
message["To"] = receiver
|
|
150
|
+
message["Subject"] = title # 邮件主题
|
|
151
|
+
body = content # 邮件正文
|
|
152
|
+
message.attach(MIMEText(body, "plain"))
|
|
153
|
+
|
|
154
|
+
# 连接到SMTP服务器并发送邮件
|
|
155
|
+
try:
|
|
156
|
+
server = smtplib.SMTP(smtp_server, port)
|
|
157
|
+
server.starttls() # 启动TLS模式
|
|
158
|
+
server.login(sender, password) # 登录你的账户
|
|
159
|
+
server.sendmail(sender, receiver, message.as_string()) # 发送邮件
|
|
160
|
+
except Exception as e:
|
|
161
|
+
print(f"Error: unable to send email. {e}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def sendMail(title: str, content: str | HtmlTableEmail, receiver: str = None, params: SMTPParams = None):
|
|
165
|
+
# 设置SMTP服务器地址和端口
|
|
166
|
+
smtp_server = params.server if params else Env.get("smtp_server")
|
|
167
|
+
port = params.port if params else Env.getInt("smtp_port") # 对于TLS,通常使用587端口;对于SSL,使用465端口
|
|
168
|
+
sender = params.sender if params else Env.get("smtp_sender") # 你的电子邮件地址
|
|
169
|
+
password = params.password if params else Env.get("smtp_password") # 你的电子邮件密码(对于某些电子邮件提供商,你可能需要生成一个应用专用密码)
|
|
170
|
+
receiver = receiver if receiver else Env.get("smtp_receiver")
|
|
171
|
+
|
|
172
|
+
if not sender or not password:
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
# 创建消息对象
|
|
176
|
+
message = MIMEMultipart()
|
|
177
|
+
message["From"] = sender
|
|
178
|
+
message["To"] = receiver
|
|
179
|
+
message["Subject"] = title # 邮件主题
|
|
180
|
+
if isinstance(content, HtmlTableEmail):
|
|
181
|
+
body = content.toHtml() # 邮件正文
|
|
182
|
+
message.attach(MIMEText(body, "html"))
|
|
183
|
+
else:
|
|
184
|
+
body = content # 邮件正文
|
|
185
|
+
message.attach(MIMEText(body, "plain"))
|
|
186
|
+
|
|
187
|
+
# 连接到SMTP服务器并发送邮件
|
|
188
|
+
try:
|
|
189
|
+
server = smtplib.SMTP(smtp_server, port)
|
|
190
|
+
server.starttls() # 启动TLS模式
|
|
191
|
+
server.login(sender, password) # 登录你的账户
|
|
192
|
+
server.sendmail(sender, receiver, message.as_string()) # 发送邮件
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(f"Error: unable to send email. {e}")
|
pybase/script.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
2
|
+
from . import net
|
|
3
|
+
from . import database
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ScriptApp:
|
|
7
|
+
_proxies = []
|
|
8
|
+
_cloudProxies = []
|
|
9
|
+
|
|
10
|
+
def __init__(self, index: int = 1, useProxy: bool = True, debug: bool = False, useCloud: bool = True) -> None:
|
|
11
|
+
self.index = index
|
|
12
|
+
self.logs = []
|
|
13
|
+
self.debug = debug
|
|
14
|
+
self.net = net.NetRequest()
|
|
15
|
+
self.cloudNet = net.CloudRequest()
|
|
16
|
+
self.cloudNet.net = self.net
|
|
17
|
+
useProxy and self.__setProxy__(index)
|
|
18
|
+
useCloud and self.__setCloudProxy__(index)
|
|
19
|
+
|
|
20
|
+
def __setProxy__(self, index: int) -> None:
|
|
21
|
+
config = database.getDataConfig("account", "proxy")
|
|
22
|
+
if not config.user or not config.host or not config.password:
|
|
23
|
+
return
|
|
24
|
+
if len(ScriptApp._proxies) == 0:
|
|
25
|
+
try:
|
|
26
|
+
sql = database.SQLHelper(user=config.user, password=config.password, port=config.port, database=config.database, host=config.host)
|
|
27
|
+
fields = ["host", "port"]
|
|
28
|
+
wheres = [["type", 1], ["state", 1]]
|
|
29
|
+
datas = sql.set_table(config.table).set_wheres(wheres).set_fields(fields).query_dict()
|
|
30
|
+
proxies = [None]
|
|
31
|
+
for data in datas:
|
|
32
|
+
proxies.append({"http": f"http://{data['host']}:{data['port']}", "https": f"http://{data['host']}:{data['port']}"})
|
|
33
|
+
ScriptApp._proxies = proxies
|
|
34
|
+
except:
|
|
35
|
+
ScriptApp._proxies = [None]
|
|
36
|
+
if len(ScriptApp._proxies):
|
|
37
|
+
self.net.proxies = ScriptApp._proxies[index % len(ScriptApp._proxies)]
|
|
38
|
+
|
|
39
|
+
def __setCloudProxy__(self, index: int) -> None:
|
|
40
|
+
config = database.getDataConfig("account", "cloud_proxy")
|
|
41
|
+
if not config.user or not config.host or not config.password:
|
|
42
|
+
return
|
|
43
|
+
if len(ScriptApp._cloudProxies) == 0:
|
|
44
|
+
try:
|
|
45
|
+
sql = database.SQLHelper(user=config.user, password=config.password, port=config.port, database=config.database, host=config.host)
|
|
46
|
+
fields = ["url", "token"]
|
|
47
|
+
wheres = [["state", 1]]
|
|
48
|
+
datas = sql.set_table(config.table).set_wheres(wheres).set_fields(fields).query_dict()
|
|
49
|
+
ScriptApp._cloudProxies = datas
|
|
50
|
+
except:
|
|
51
|
+
ScriptApp._cloudProxies = []
|
|
52
|
+
if len(ScriptApp._cloudProxies):
|
|
53
|
+
self.cloudNet.cloud = ScriptApp._cloudProxies[index % len(ScriptApp._cloudProxies)]
|
|
54
|
+
|
|
55
|
+
def __log__(self, msg: str | dict = "", flush: bool = False):
|
|
56
|
+
if flush or self.debug:
|
|
57
|
+
print(msg, flush=True)
|
|
58
|
+
else:
|
|
59
|
+
if isinstance(msg, str):
|
|
60
|
+
self.logs.append(msg)
|
|
61
|
+
else:
|
|
62
|
+
self.logs.append(str(msg))
|
|
63
|
+
|
|
64
|
+
def __runFunc__(self, fun: callable, *args, **kwargs):
|
|
65
|
+
try:
|
|
66
|
+
return fun(*args, **kwargs)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
self.__log__(e)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InviteObj:
|
|
72
|
+
id = ""
|
|
73
|
+
accpet: int = 0
|
|
74
|
+
send: int = 0
|
|
75
|
+
params = {}
|
|
76
|
+
target: list[dict] = []
|
|
77
|
+
|
|
78
|
+
def __init__(self):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def threadPool(func, args, num=5):
|
|
83
|
+
# 启用线程池
|
|
84
|
+
executor = ThreadPoolExecutor(max_workers=num)
|
|
85
|
+
tasks = [executor.submit(func, arg, index + 1) for index, arg in enumerate(args)]
|
|
86
|
+
results = []
|
|
87
|
+
for future in as_completed(tasks):
|
|
88
|
+
data = future.result()
|
|
89
|
+
results.append(data)
|
|
90
|
+
return results
|
pybase/util.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
2
|
+
from datetime import date, datetime, timedelta
|
|
3
|
+
import random
|
|
4
|
+
import string
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def generateRandomPhoneUA():
|
|
8
|
+
# 手机设备品牌与型号
|
|
9
|
+
devices = ["Samsung Galaxy S21", "Xiaomi Mi 11", "Google Pixel 6", "Huawei P40", "OPPO Reno5", "Vivo X60", "OnePlus 9"]
|
|
10
|
+
|
|
11
|
+
# 操作系统版本
|
|
12
|
+
os_versions = ["Android 11", "Android 12", "Android 13"]
|
|
13
|
+
|
|
14
|
+
# 浏览器类型
|
|
15
|
+
browsers = ["Chrome"]
|
|
16
|
+
|
|
17
|
+
# 随机选择设备、操作系统和浏览器
|
|
18
|
+
device = random.choice(devices)
|
|
19
|
+
os_version = random.choice(os_versions)
|
|
20
|
+
browser = random.choice(browsers)
|
|
21
|
+
|
|
22
|
+
# 构建完整的User-Agent字符串
|
|
23
|
+
ua_base = f"Mozilla/5.0 ({device}; Android {os_version}; Mobile) AppleWebKit"
|
|
24
|
+
ua = f"{ua_base}/537.36 (KHTML, like Gecko) {browser}/{random.randint(80, 99)}.0.{random.randint(0, 99)} Mobile Safari/537.36"
|
|
25
|
+
|
|
26
|
+
return ua
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def generateRandomStr(len: int):
|
|
30
|
+
return "".join(random.choices(string.ascii_letters + string.digits, k=len))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def getNextHourSeconds() -> float:
|
|
34
|
+
# 获取当前时间
|
|
35
|
+
now = datetime.now()
|
|
36
|
+
# 计算下一小时的时间点
|
|
37
|
+
next_hour = (now + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0)
|
|
38
|
+
# 计算距离下一小时的总秒数
|
|
39
|
+
seconds_until_next_hour = (next_hour - now).total_seconds()
|
|
40
|
+
return seconds_until_next_hour
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def formatTimestamp(ts: int | float | str = None) -> str:
|
|
44
|
+
if isinstance(ts, int) or isinstance(ts, float):
|
|
45
|
+
return datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")
|
|
46
|
+
elif ts == None:
|
|
47
|
+
return datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
48
|
+
else:
|
|
49
|
+
return ts
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def formatDate(ts: int, split="") -> str:
|
|
53
|
+
return datetime.fromtimestamp(ts).strftime(f"%Y{split}%m{split}%d")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def isLastDayOfMonth():
|
|
57
|
+
today = date.today()
|
|
58
|
+
next_day = today + timedelta(days=1)
|
|
59
|
+
return next_day.month != today.month
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def daysDifference(date_str: str) -> int:
|
|
63
|
+
# 尝试将输入的日期字符串转换为datetime对象
|
|
64
|
+
try:
|
|
65
|
+
# 假设输入的日期格式是'yyyy-mm-dd'
|
|
66
|
+
input_date = datetime.strptime(date_str, "%Y-%m-%d").date()
|
|
67
|
+
except ValueError:
|
|
68
|
+
# 如果日期格式不正确,返回错误信息或处理异常
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
# 获取今天的日期
|
|
72
|
+
today = datetime.now().date()
|
|
73
|
+
|
|
74
|
+
# 计算两个日期之间的天数差
|
|
75
|
+
delta = today - input_date
|
|
76
|
+
days_diff = delta.days
|
|
77
|
+
|
|
78
|
+
return days_diff
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def threadPool(func, args, num=5):
|
|
82
|
+
# 启用线程池
|
|
83
|
+
executor = ThreadPoolExecutor(max_workers=num)
|
|
84
|
+
tasks = [executor.submit(func, arg, index + 1) for index, arg in enumerate(args)]
|
|
85
|
+
for future in as_completed(tasks):
|
|
86
|
+
data = future.result()
|
pybase/webview.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
from time import sleep
|
|
3
|
+
from . import env
|
|
4
|
+
from selenium import webdriver
|
|
5
|
+
from traceback import format_exc
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from selenium.webdriver.common.by import By
|
|
9
|
+
|
|
10
|
+
ENV_NAME = "remote_selenium"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def buildDriver(options: webdriver.ChromeOptions, rm: bool = True) -> webdriver.Chrome:
|
|
14
|
+
"""
|
|
15
|
+
获取一个WebDriver实例
|
|
16
|
+
环境变量: remote_selenium 远程地址
|
|
17
|
+
"""
|
|
18
|
+
remote = env.get(ENV_NAME)
|
|
19
|
+
if rm and remote:
|
|
20
|
+
return webdriver.Remote(command_executor=remote, options=options)
|
|
21
|
+
else:
|
|
22
|
+
return webdriver.Chrome(options=options)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def getUserDir(project: str, user: str):
|
|
26
|
+
"""
|
|
27
|
+
获取用户数据目录
|
|
28
|
+
"""
|
|
29
|
+
if platform.system().lower() == "windows":
|
|
30
|
+
dirs = ["E:", "cache", "selenium", project, user]
|
|
31
|
+
else:
|
|
32
|
+
dirs = [str(Path.home()), "cache", "selenium", project, user]
|
|
33
|
+
return os.sep.join(dirs)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def clearUserData(driver: webdriver.Chrome):
|
|
37
|
+
"""
|
|
38
|
+
清除用户数据
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
driver.get("chrome://settings/clearBrowserData")
|
|
42
|
+
sleep(2)
|
|
43
|
+
checkes = driver.find_elements(By.CLASS_NAME, "settings-checkbox")
|
|
44
|
+
for checke in checkes:
|
|
45
|
+
label = checke.text.lower()
|
|
46
|
+
checked = checke.get_attribute("no-set-pref").lower()
|
|
47
|
+
if label.find("cookie") >= 0 and checked == "checked":
|
|
48
|
+
checke.click()
|
|
49
|
+
elif label.find("cookie") <= 0 and checked != "checked":
|
|
50
|
+
checke.click()
|
|
51
|
+
clearButton = driver.find_element(By.ID, "clearButton")
|
|
52
|
+
# clearButton.click()
|
|
53
|
+
except:
|
|
54
|
+
print("clearUserData", format_exc())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyscriptbase
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: python script base library
|
|
5
|
+
Home-page:
|
|
6
|
+
Author: ASMan
|
|
7
|
+
Author-email:
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: loguru
|
|
10
|
+
Requires-Dist: selenium
|
|
11
|
+
Requires-Dist: pymysql
|
|
12
|
+
Requires-Dist: pycryptodome
|
|
13
|
+
Requires-Dist: pyyaml
|
|
14
|
+
Requires-Dist: xmltodict
|
|
15
|
+
Requires-Dist: prettytable
|
|
16
|
+
Requires-Dist: pyjwt
|
|
17
|
+
Requires-Dist: fake-useragent
|
|
18
|
+
Requires-Dist: beautifulsoup4
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
Requires-Dist: lxml
|
|
21
|
+
|
|
22
|
+
# Instructions
|
|
23
|
+
|
|
24
|
+
python script base library
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
pybase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
pybase/cipher.py,sha256=Bz5lqG9GuooOt2y4u495VA9BE5qAV3fPnouR8qzV7K4,5321
|
|
3
|
+
pybase/database.py,sha256=xOBKuyBS7RRTnuaaP5VB1XRaBQ8P7sNBEeabJfRQhVc,13302
|
|
4
|
+
pybase/env.py,sha256=7MqnRXEq6raicxfC-bA9jCczo63Gh9LjQbBO5VtJ7rY,2944
|
|
5
|
+
pybase/net.py,sha256=WvExRZoA5iHpx4qtE0YV_Mf_lVNaYvQNwtp0Suzih4A,11883
|
|
6
|
+
pybase/panel.py,sha256=1e9sod303rkuVJOuuym9jK-ZXUzkBom9x-svdnpZnjI,6827
|
|
7
|
+
pybase/pusher.py,sha256=lE4X3epadRaHKrRs8vBZW1T35mD03QtG-1Bu9okvq5Q,7054
|
|
8
|
+
pybase/script.py,sha256=MCw19l8JOeXxhAOHr8NhUMhMDGnLo8aM_F2zklZldYY,3486
|
|
9
|
+
pybase/util.py,sha256=RxQibRYGZKG5YCC6ffc3FGsE4nMkawknAtrEhGrxSig,2813
|
|
10
|
+
pybase/webview.py,sha256=NQkJuqBlVlC2X_wTcfhtrxUBoP4ie-VCfgBud5YjhD4,1687
|
|
11
|
+
pyscriptbase-1.0.1.dist-info/METADATA,sha256=Iutu3GqD2xVj2aNKOMEjsLXdm9qPLgIM05q82c_rKg0,537
|
|
12
|
+
pyscriptbase-1.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
13
|
+
pyscriptbase-1.0.1.dist-info/top_level.txt,sha256=gYgKDnq77vSbZG-_hkku2uwMNI_t6Z7_WQq4dek8Al4,7
|
|
14
|
+
pyscriptbase-1.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pybase
|