ezKit 1.11.0__py3-none-any.whl → 1.11.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.
ezKit/_file.py ADDED
@@ -0,0 +1,36 @@
1
+ from loguru import logger
2
+
3
+ from . import utils
4
+
5
+
6
+ def markdown_to_html(markdown_file: str, html_file: str, header_file: str = "markdown.html") -> bool:
7
+ """Markdown to HTML"""
8
+
9
+ # Markdown to HTML
10
+ # 使用 MacDown 生成 HTML, 然后提取样式到 markdown.html
11
+ # pandoc 生成的 HTML 默认 max-width: 36em, 如果表格内容很长, 会导致表格样式难看
12
+ # 所以在 markdown.html 的 body{...} 中添加配置 max-width: unset, 解决内容过长的样式问题
13
+ # 所有 a 标签添加 text-decoration: none; 去除链接下划线
14
+ # pandoc --no-highlight -s --quiet -f markdown -t html -H markdown.html -o data.html data.md
15
+
16
+ info: str = "markdown to html"
17
+
18
+ logger.info(f"{info} ......")
19
+
20
+ try:
21
+
22
+ result = utils.shell(
23
+ f"pandoc --no-highlight -s --quiet -f markdown -t html -H {header_file} -o {html_file} {markdown_file}"
24
+ )
25
+
26
+ if result is None or result.returncode != 0:
27
+ logger.error(f"{info} [failure]")
28
+ return False
29
+
30
+ logger.success(f"{info} [success]")
31
+ return True
32
+
33
+ except Exception as e:
34
+ logger.error(f"{info} [failure]")
35
+ logger.exception(e)
36
+ return False
ezKit/qywx.py CHANGED
@@ -1,10 +1,38 @@
1
1
  """企业微信"""
2
+ #
2
3
  # 企业微信开发者中心
3
4
  # https://developer.work.weixin.qq.com/
4
5
  # https://developer.work.weixin.qq.com/document/path/90313 (全局错误码)
5
6
  # 参考文档:
6
7
  # https://www.gaoyuanqi.cn/python-yingyong-qiyewx/
7
8
  # https://www.jianshu.com/p/020709b130d3
9
+ #
10
+ # 应用管理
11
+ # https://work.weixin.qq.com/wework_admin/frame#apps
12
+ # 自建 -> 创建应用
13
+ # https://work.weixin.qq.com/wework_admin/frame#apps/createApiApp
14
+ # 上传 Logo -> 应用名称 -> 选择部门 / 成员 -> 创建应用
15
+ #
16
+ # 服务端API
17
+ # https://developer.work.weixin.qq.com/document/path/90664
18
+ # 基本概念
19
+ # https://developer.work.weixin.qq.com/document/path/90665
20
+ # 企业ID:
21
+ # https://work.weixin.qq.com/wework_admin/frame#profile
22
+ # AgentId 和 Secret:
23
+ # 进入已创建的应用, 即可获取
24
+ #
25
+ # 应用管理 -> 应用 -> 开发者接口 -> 网页授权及JS-SDK
26
+ #
27
+ # 可作为应用OAuth2.0网页授权功能的回调域名
28
+ # 下载 配置可信域名需完成域名归属认证 的文件
29
+ # 保存到域名下
30
+ # 配置可信域名需完成域名归属认证 已验证
31
+ #
32
+ # 应用管理 -> 应用 -> 开发者接口 -> 企业可信IP
33
+ #
34
+ # 添加IP
35
+ #
8
36
  import json
9
37
  import time
10
38
 
@@ -17,150 +45,217 @@ from . import utils
17
45
  class QYWX:
18
46
  """企业微信"""
19
47
 
20
- url_prefix = "https://qyapi.weixin.qq.com"
21
- work_id: str | None = None
22
- agent_id: str | None = None
23
- agent_secret: str | None = None
24
- access_token: str | None = None
48
+ # API前缀
49
+ api_prefix = "https://qyapi.weixin.qq.com"
25
50
 
26
- def __init__(self, work_id: str | None, agent_id: str | None, agent_secret: str | None):
51
+ # 企业ID: https://developer.work.weixin.qq.com/document/path/90665#corpid
52
+ work_id: str = ""
53
+
54
+ # 应用ID: https://developer.work.weixin.qq.com/document/path/90665#agentid
55
+ agent_id: str = ""
56
+
57
+ # 应用Secret: https://developer.work.weixin.qq.com/document/path/90665#secret
58
+ agent_secret: str = ""
59
+
60
+ # Token: https://developer.work.weixin.qq.com/document/path/90665#access-token
61
+ access_token: str = ""
62
+
63
+ def __init__(self, work_id: str, agent_id: str, agent_secret: str):
27
64
  """Initiation"""
28
65
  self.work_id = work_id
29
66
  self.agent_id = agent_id
30
67
  self.agent_secret = agent_secret
31
68
 
32
69
  """获取 Token"""
33
- self.getaccess_token()
70
+ self.get_access_token()
71
+
72
+ def get_access_token(self) -> bool:
73
+ """获取Token"""
74
+
75
+ info: str = "获取 Token"
34
76
 
35
- def getaccess_token(self) -> str | None:
36
77
  try:
37
- response = requests.get(f"{self.url_prefix}/cgi-bin/gettoken?corpid={self.work_id}&corpsecret={self.agent_secret}", timeout=10)
78
+
79
+ logger.info(f"{info} ......")
80
+
81
+ response = requests.get(f"{self.api_prefix}/cgi-bin/gettoken?corpid={self.work_id}&corpsecret={self.agent_secret}", timeout=10)
38
82
 
39
83
  if response.status_code != 200:
40
- self.access_token = None
41
- return None
84
+ logger.error(f"{info} [状态码错误]")
85
+ return False
42
86
 
43
87
  result: dict = response.json()
44
- self.access_token = result.get('access_token')
45
- return result.get('access_token')
46
88
 
47
- except Exception as e:
48
- logger.exception(e)
49
- return None
89
+ self.access_token = result.get("access_token", "")
50
90
 
51
- def get_agent_list(self) -> dict | str | None:
52
- try:
53
- if self.access_token is None:
54
- self.getaccess_token()
55
- response = requests.get(f"{self.url_prefix}/cgi-bin/agent/list?access_token={self.access_token}", timeout=10)
56
- if response.status_code == 200:
57
- response_data: dict = response.json()
58
- if response_data.get('errcode') == 42001:
59
- self.getaccess_token()
60
- time.sleep(1)
61
- self.get_agent_list()
62
- return response_data
63
- return response.text
64
- except Exception as e:
65
- logger.exception(e)
66
- return None
91
+ if not utils.isTrue(self.access_token, str):
92
+ logger.error(f"{info} [失败]")
93
+ return False
67
94
 
68
- def get_department_list(self, eid: str | None = None) -> dict | str | None:
69
- """eid: Enterprise ID"""
70
- try:
71
- if self.access_token is None:
72
- self.getaccess_token()
73
- response = requests.get(f"{self.url_prefix}/cgi-bin/department/list?access_token={self.access_token}&id={eid}", timeout=10)
74
- if response.status_code == 200:
75
- response_data: dict = response.json()
76
- if response_data.get('errcode') == 42001:
77
- self.getaccess_token()
78
- time.sleep(1)
79
- self.get_department_list(eid)
80
- return response_data
81
- return response.text
82
- except Exception as e:
83
- logger.exception(e)
84
- return None
95
+ logger.success(f"{info} [成功]")
96
+ return True
85
97
 
86
- def get_user_list(self, eid: str | None = None) -> dict | str | None:
87
- """eid: Enterprise ID"""
88
- try:
89
- if self.access_token is None:
90
- self.getaccess_token()
91
- response = requests.get(f"{self.url_prefix}/cgi-bin/user/list?access_token={self.access_token}&department_id={eid}", timeout=10)
92
- if response.status_code == 200:
93
- response_data: dict = response.json()
94
- if response_data.get('errcode') == 42001:
95
- self.getaccess_token()
96
- time.sleep(1)
97
- self.get_user_list(eid)
98
- return response_data
99
- return response.text
100
98
  except Exception as e:
99
+ logger.success(f"{info} [失败]")
101
100
  logger.exception(e)
101
+ return False
102
+
103
+ # def get_agent_list(self) -> dict | str | None:
104
+ # try:
105
+ # if not utils.isTrue(self.access_token, str):
106
+ # self.get_access_token()
107
+ # response = requests.get(f"{self.api_prefix}/cgi-bin/agent/list?access_token={self.access_token}", timeout=10)
108
+ # if response.status_code == 200:
109
+ # response_data: dict = response.json()
110
+ # if response_data.get('errcode') == 42001:
111
+ # self.get_access_token()
112
+ # time.sleep(1)
113
+ # self.get_agent_list()
114
+ # return response_data
115
+ # return response.text
116
+ # except Exception as e:
117
+ # logger.exception(e)
118
+ # return None
119
+
120
+ # def get_department_list(self, eid: str | None = None) -> dict | str | None:
121
+ # """eid: Enterprise ID"""
122
+ # try:
123
+ # if self.access_token is None:
124
+ # self.get_access_token()
125
+ # response = requests.get(f"{self.api_prefix}/cgi-bin/department/list?access_token={self.access_token}&id={eid}", timeout=10)
126
+ # if response.status_code == 200:
127
+ # response_data: dict = response.json()
128
+ # if response_data.get('errcode') == 42001:
129
+ # self.get_access_token()
130
+ # time.sleep(1)
131
+ # self.get_department_list(eid)
132
+ # return response_data
133
+ # return response.text
134
+ # except Exception as e:
135
+ # logger.exception(e)
136
+ # return None
137
+
138
+ # def get_user_list(self, eid: str | None = None) -> dict | str | None:
139
+ # """eid: Enterprise ID"""
140
+ # try:
141
+ # if self.access_token is None:
142
+ # self.get_access_token()
143
+ # response = requests.get(f"{self.api_prefix}/cgi-bin/user/list?access_token={self.access_token}&department_id={eid}", timeout=10)
144
+ # if response.status_code == 200:
145
+ # response_data: dict = response.json()
146
+ # if response_data.get('errcode') == 42001:
147
+ # self.get_access_token()
148
+ # time.sleep(1)
149
+ # self.get_user_list(eid)
150
+ # return response_data
151
+ # return response.text
152
+ # except Exception as e:
153
+ # logger.exception(e)
154
+ # return None
155
+
156
+ def get_user_id_by_mobile(self, mobile: str) -> dict | None:
157
+ """根据电话号码获取用户ID"""
158
+
159
+ info: str = f"根据电话号码获取用户ID: {mobile}"
160
+
161
+ if not utils.check_arguments([(mobile, str, "mobile")]):
162
+ logger.error(f"{info} [错误]")
102
163
  return None
103
164
 
104
- def get_user_id_by_mobile(self, mobile: str | None = None) -> dict | str | None:
105
165
  try:
106
- if self.access_token is None:
107
- self.getaccess_token()
166
+
167
+ logger.info(f"{info} ......")
168
+
169
+ if not utils.isTrue(self.access_token, str):
170
+ self.get_access_token()
171
+
108
172
  json_string = json.dumps({'mobile': mobile})
109
- response = requests.post(f"{self.url_prefix}/cgi-bin/user/getuserid?access_token={self.access_token}", data=json_string, timeout=10)
110
- if response.status_code == 200:
111
- response_data: dict = response.json()
112
- if response_data.get('errcode') == 42001:
113
- self.getaccess_token()
114
- time.sleep(1)
115
- self.get_user_id_by_mobile(mobile)
116
- return response_data
117
- return response.text
118
- except Exception as e:
119
- logger.exception(e)
120
- return None
121
173
 
122
- def get_user_info(self, eid: str | None = None) -> dict | str | None:
123
- """eid: Enterprise ID"""
124
- try:
125
- if self.access_token is None:
126
- self.getaccess_token()
127
- response = requests.get(f"{self.url_prefix}/cgi-bin/user/get?access_token={self.access_token}&userid={eid}", timeout=10)
128
- if response.status_code == 200:
129
- response_data: dict = response.json()
130
- if response_data.get('errcode') == 42001:
131
- self.getaccess_token()
132
- time.sleep(1)
133
- self.get_user_info(eid)
134
- return response_data
135
- return response.text
174
+ response = requests.post(f"{self.api_prefix}/cgi-bin/user/getuserid?access_token={self.access_token}", data=json_string, timeout=10)
175
+
176
+ if response.status_code != 200:
177
+ logger.error(f"{info} [接口请求错误]")
178
+ return None
179
+
180
+ response_data: dict = response.json()
181
+
182
+ if response_data.get('errcode') == 42001:
183
+ self.get_access_token()
184
+ time.sleep(1)
185
+ self.get_user_id_by_mobile(mobile)
186
+
187
+ logger.success(f"{info} [成功]")
188
+
189
+ return response_data
190
+
191
+ # return response.text
192
+
136
193
  except Exception as e:
194
+ logger.error(f"{info} [失败]")
137
195
  logger.exception(e)
138
196
  return None
139
197
 
198
+ # def get_user_info(self, eid: str | None = None) -> dict | str | None:
199
+ # """eid: Enterprise ID"""
200
+ # try:
201
+ # if self.access_token is None:
202
+ # self.get_access_token()
203
+ # response = requests.get(f"{self.api_prefix}/cgi-bin/user/get?access_token={self.access_token}&userid={eid}", timeout=10)
204
+ # if response.status_code == 200:
205
+ # response_data: dict = response.json()
206
+ # if response_data.get('errcode') == 42001:
207
+ # self.get_access_token()
208
+ # time.sleep(1)
209
+ # self.get_user_info(eid)
210
+ # return response_data
211
+ # return response.text
212
+ # except Exception as e:
213
+ # logger.exception(e)
214
+ # return None
215
+
140
216
  def send_message_by_mobile(self, mobile: str | list, message: str) -> bool:
141
217
  """发送消息"""
142
218
 
143
219
  # 参考文档:
144
220
  # https://developer.work.weixin.qq.com/document/path/90235
145
221
 
222
+ info: str = "发送消息"
223
+
224
+ if not utils.check_arguments([(mobile, (str, list), "mobile"), (message, str, "message")]):
225
+ logger.error(f"{info} [失败]")
226
+ return False
227
+
146
228
  try:
147
- if self.access_token is None:
148
- self.getaccess_token()
149
229
 
150
- users: list = []
230
+ logger.info(f"{info} ......")
151
231
 
152
- match True:
153
- case True if isinstance(mobile, list) and utils.isTrue(mobile, list):
154
- users = mobile
155
- case True if isinstance(mobile, str) and utils.isTrue(mobile, str):
156
- users.append(mobile)
157
- case _:
232
+ if not utils.isTrue(self.access_token, str):
233
+ if not self.get_access_token():
234
+ logger.error(f"{info} [失败]")
158
235
  return False
159
236
 
237
+ users: list = []
238
+
239
+ if isinstance(mobile, str) and utils.isTrue(mobile, str):
240
+ users.append(mobile)
241
+ elif isinstance(mobile, list) and utils.isTrue(mobile, list):
242
+ users += mobile
243
+ else:
244
+ logger.error(f"{info} [电话号码错误]")
245
+ return False
246
+
160
247
  for user in users:
248
+
249
+ logger.info(f"{info} [用户 {user}]")
250
+
161
251
  user_object = self.get_user_id_by_mobile(user)
162
252
 
163
- if not isinstance(user_object, dict):
253
+ if not (isinstance(user_object, dict) and utils.isTrue(user_object, dict)):
254
+ logger.error(f"{info} [获取用户ID错误: {user}]")
255
+ continue
256
+
257
+ if user_object.get("errcode", -1) != 0 or user_object.get("errmsg", "") != "ok":
258
+ logger.error(f"{user_object.get('errcode')}: {user_object.get('errmsg')}")
164
259
  continue
165
260
 
166
261
  json_dict = {
@@ -173,17 +268,29 @@ class QYWX:
173
268
  'enable_duplicate_check': 0,
174
269
  'duplicate_check_interval': 1800
175
270
  }
271
+
176
272
  json_string = json.dumps(json_dict)
177
- response = requests.post(f"{self.url_prefix}/cgi-bin/message/send?access_token={self.access_token}", data=json_string, timeout=10)
178
- if response.status_code == 200:
179
- response_data: dict = response.json()
180
- if response_data.get('errcode') == 42001:
181
- self.getaccess_token()
182
- time.sleep(1)
183
- self.send_message_by_mobile(mobile, message)
273
+
274
+ response = requests.post(f"{self.api_prefix}/cgi-bin/message/send?access_token={self.access_token}", data=json_string, timeout=10)
275
+
276
+ if response.status_code != 200:
277
+ logger.error(f"{info} [发送消息失败: {user}]")
278
+ continue
279
+
280
+ response_data: dict = response.json()
281
+
282
+ if response_data.get('errcode') == 42001:
283
+ self.get_access_token()
284
+ time.sleep(1)
285
+ self.send_message_by_mobile(mobile, message)
286
+
287
+ logger.success(f"{info} [成功: 用户 {user}]")
288
+
289
+ logger.success(f"{info} [完成]")
184
290
 
185
291
  return True
186
292
 
187
293
  except Exception as e:
294
+ logger.error(f"{info} [失败]")
188
295
  logger.exception(e)
189
296
  return False
ezKit/utils.py CHANGED
@@ -418,26 +418,19 @@ def list_print_by_step(
418
418
 
419
419
  try:
420
420
 
421
- # result: list = []
422
-
423
- # if len(data) <= step:
424
- # result.append(data)
425
- # else:
426
- # data_list = list_split(data, step, debug=debug)
427
- # if data_list is None or isTrue(data_list, list) is False:
428
- # return False
429
- # result = data_list
430
-
431
- # for item in result:
432
- # print(*item, sep=separator)
433
-
434
- # return True
435
-
436
421
  # 打印
437
422
  for i, v in enumerate(data):
423
+
438
424
  if i > 0 and i % step == 0:
439
425
  print()
440
- print(v, end=separator)
426
+
427
+ # print(v, end=separator)
428
+
429
+ # 每行最后一个 或者 所有数据最后一个, 不打印分隔符
430
+ if ((i % step) == (step - 1)) or ((i + 1) == len(data)):
431
+ print(v, end='')
432
+ else:
433
+ print(v, end=separator)
441
434
 
442
435
  print()
443
436
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.11.0
3
+ Version: 1.11.2
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -1,18 +1,19 @@
1
1
  ezKit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ezKit/_file.py,sha256=0qRZhwYuagTgTGrhm-tzAMvEQT4HTJA_xZKqF2bo0ho,1207
2
3
  ezKit/bottle.py,sha256=usKK1wVaZw4_D-4VwMYmOIc8jtz4TrpM30nck59HMFw,180178
3
4
  ezKit/bottle_extensions.py,sha256=3reEQVZuHklXTl6r7F8kiBFFPb0RaAGc3mYJJnrMDjQ,1129
4
5
  ezKit/cipher.py,sha256=0T_StbjiNI4zgrjVgcfU-ffKgu1waBA9UDudAnqFcNM,2896
5
6
  ezKit/database.py,sha256=r5YNoEzeOeVTlEWI99xXtHTmPZ73_DopS8DTzZk8Lts,12432
6
7
  ezKit/http.py,sha256=ysXzqXFi9zmuVKINbYGwmf9Q5xDVW_DZWrSh6HSVq8M,1800
7
8
  ezKit/mongo.py,sha256=l3jRMmoGrTm16OG4daSCn0JLU1nbYAmTtHokwjLXzoA,2390
8
- ezKit/qywx.py,sha256=X_H4fzP-iEqeDEbumr7D1bXi6dxczaxfO8iyutzy02s,7171
9
+ ezKit/qywx.py,sha256=GKwcx_L3WCcWEMZuuMpqdAy9ubqSu2RoxndxkkAYRtQ,10301
9
10
  ezKit/redis.py,sha256=tdiqfizPYQQTIUumkJGUJsJVlv0zVTSTYGQN0QutYs4,1963
10
11
  ezKit/sendemail.py,sha256=tRXCsJm_RfTJ9xEWe_lTQ5kOs2JxHGPXvq0oWA7prq0,7263
11
12
  ezKit/token.py,sha256=HKREyZj_T2S8-aFoFIrBXTaCKExQq4zE66OHXhGHqQg,1750
12
- ezKit/utils.py,sha256=CeRFReOm3afxRYkqaVXWv1x4eKXB1YiHnbeGBxvWhKs,42657
13
+ ezKit/utils.py,sha256=a2RiwOjO83Wi4GmyBRLI1UreaVgNLL66Snbq0HnI37c,42501
13
14
  ezKit/xftp.py,sha256=XyIdr_2rxRVLqPofG6fIYWhAMVsFwTyp46dg5P9FLW4,7774
14
- ezKit-1.11.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
15
- ezKit-1.11.0.dist-info/METADATA,sha256=qSEnzYClSlWB_0w9rYxDuVD-tc9Y7hPIv_sw3GuXo8g,191
16
- ezKit-1.11.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
17
- ezKit-1.11.0.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
18
- ezKit-1.11.0.dist-info/RECORD,,
15
+ ezKit-1.11.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
16
+ ezKit-1.11.2.dist-info/METADATA,sha256=O6yYp94ImOfIjj7pLEWeFsCl1PDBeJNk9nPgme9iOAs,191
17
+ ezKit-1.11.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
+ ezKit-1.11.2.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
19
+ ezKit-1.11.2.dist-info/RECORD,,
File without changes