pixelarraylib 1.0.7__tar.gz → 1.0.9__tar.gz

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 (47) hide show
  1. {pixelarraylib-1.0.7/pixelarraylib.egg-info → pixelarraylib-1.0.9}/PKG-INFO +1 -1
  2. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/__init__.py +1 -1
  3. pixelarraylib-1.0.9/pixelarraylib/aliyun/acr.py +514 -0
  4. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/aliyun_email.py +124 -2
  5. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/billing.py +3 -3
  6. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/content_scanner.py +6 -6
  7. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/eci.py +6 -6
  8. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/eip.py +3 -3
  9. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/fc.py +2 -2
  10. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/oss.py +5 -5
  11. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/sms.py +61 -2
  12. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/sts.py +3 -3
  13. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/db_utils/mysql.py +10 -10
  14. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/db_utils/redis.py +18 -18
  15. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/decorators/decorators.py +2 -2
  16. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/monitor/feishu.py +64 -1
  17. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/__init__.py +1 -1
  18. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/system/common.py +6 -8
  19. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9/pixelarraylib.egg-info}/PKG-INFO +1 -1
  20. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pyproject.toml +1 -1
  21. pixelarraylib-1.0.7/pixelarraylib/aliyun/acr.py +0 -265
  22. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/LICENSE +0 -0
  23. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/MANIFEST.in +0 -0
  24. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/README.md +0 -0
  25. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/__main__.py +0 -0
  26. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/__init__.py +0 -0
  27. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/domain.py +0 -0
  28. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/aliyun/ecs.py +0 -0
  29. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/decorators/__init__.py +0 -0
  30. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/__init__.py +0 -0
  31. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/code_analyzer.py +0 -0
  32. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/gitlab/pypi_package_manager.py +0 -0
  33. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/monitor/__init__.py +0 -0
  34. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/net/request.py +0 -0
  35. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/build_website.py +0 -0
  36. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/collect_code_to_txt.py +0 -0
  37. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/create_test_case_files.py +0 -0
  38. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/nginx_proxy_to_ecs.py +0 -0
  39. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/scripts/remove_empty_lines.py +0 -0
  40. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib/system/__init__.py +0 -0
  41. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/SOURCES.txt +0 -0
  42. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/dependency_links.txt +0 -0
  43. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/entry_points.txt +0 -0
  44. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/requires.txt +0 -0
  45. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/pixelarraylib.egg-info/top_level.txt +0 -0
  46. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/requirements.txt +0 -0
  47. {pixelarraylib-1.0.7 → pixelarraylib-1.0.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraylib
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: PixelArray Python开发工具库 - 包含阿里云服务、数据库工具、装饰器、监控等功能
5
5
  Author-email: Lu qi <qi.lu@pixelarrayai.com>
6
6
  License-Expression: MIT
@@ -20,7 +20,7 @@ PixelArray Python开发工具库
20
20
  from pixelarraylib.gitlab import pypi_package_manager
21
21
  """
22
22
 
23
- __version__ = "1.0.7"
23
+ __version__ = "1.0.9"
24
24
  __author__ = "PixelArray"
25
25
  __email__ = "qi.lu@pixelarrayai.com"
26
26
 
@@ -0,0 +1,514 @@
1
+ import os
2
+ import sys
3
+ import re
4
+
5
+ from typing import List
6
+
7
+ from alibabacloud_cr20181201.client import Client as cr20181201Client
8
+ from alibabacloud_credentials.client import Client as CredentialClient
9
+ from alibabacloud_tea_openapi import models as open_api_models
10
+ from alibabacloud_cr20181201 import models as cr_20181201_models
11
+ from alibabacloud_tea_util import models as util_models
12
+ from alibabacloud_tea_util.client import Client as UtilClient
13
+
14
+ from pixelarraylib.monitor.feishu import Feishu
15
+
16
+ feishu_alert = Feishu("devtoolkit服务报警")
17
+
18
+
19
+ class ACRUtils:
20
+ def __init__(self, region_id: str, access_key_id: str, access_key_secret: str):
21
+ self.region_id = region_id
22
+ self.access_key_id = access_key_id
23
+ self.access_key_secret = access_key_secret
24
+ self.client = self._create_client()
25
+
26
+ def _create_client(self):
27
+ credential = CredentialClient()
28
+ config = open_api_models.Config(
29
+ credential=credential,
30
+ access_key_id=self.access_key_id,
31
+ access_key_secret=self.access_key_secret,
32
+ )
33
+ # Endpoint 请参考 https://api.aliyun.com/product/cr
34
+ config.endpoint = f"cr.{self.region_id}.aliyuncs.com"
35
+ return cr20181201Client(config)
36
+
37
+ def list_instances(self):
38
+ """
39
+ description:
40
+ 列出容器镜像服务实例
41
+ parameters:
42
+ None
43
+ return:
44
+ dict: 包含实例列表的响应数据
45
+ success(bool): 是否成功
46
+ """
47
+
48
+ try:
49
+ request = cr_20181201_models.ListInstanceRequest()
50
+ response = self.client.list_instance(request)
51
+ return response.body.to_map(), True
52
+ except Exception as e:
53
+ print(f"列出实例失败: {e}")
54
+ return {}, False
55
+
56
+ def create_namespace(self, instance_id: str, namespace_name: str):
57
+ """
58
+ description:
59
+ 创建命名空间
60
+ parameters:
61
+ instance_id (str): 实例ID
62
+ namespace_name (str): 命名空间名称
63
+ return:
64
+ dict: 创建结果
65
+ success(bool): 是否成功
66
+ """
67
+
68
+ try:
69
+ request = cr_20181201_models.CreateNamespaceRequest(
70
+ instance_id=instance_id, namespace_name=namespace_name
71
+ )
72
+ response = self.client.create_namespace(request)
73
+ return response.body.to_map(), True
74
+ except Exception as e:
75
+ print(f"创建命名空间失败: {e}")
76
+ return {}, False
77
+
78
+ def delete_namespace(self, instance_id: str, namespace_name: str):
79
+ """
80
+ description:
81
+ 删除命名空间
82
+ parameters:
83
+ instance_id (str): 实例ID
84
+ namespace_name (str): 命名空间名称
85
+ return:
86
+ dict: 删除结果
87
+ success(bool): 是否成功
88
+ """
89
+ try:
90
+ request = cr_20181201_models.DeleteNamespaceRequest(
91
+ instance_id=instance_id, namespace_name=namespace_name
92
+ )
93
+ response = self.client.delete_namespace(request)
94
+ return response.body.to_map(), True
95
+ except Exception as e:
96
+ print(f"删除命名空间失败: {e}")
97
+ return {}, False
98
+
99
+ def list_namespaces(self, instance_id: str):
100
+ """
101
+ description:
102
+ 列出命名空间
103
+ parameters:
104
+ instance_id (str): 实例ID
105
+ return:
106
+ dict: 包含命名空间列表的响应数据
107
+ success(bool): 是否成功
108
+ """
109
+ try:
110
+ request = cr_20181201_models.ListNamespaceRequest(instance_id=instance_id)
111
+ response = self.client.list_namespace(request)
112
+ return response.body.to_map(), True
113
+ except Exception as e:
114
+ print(f"列出命名空间失败: {e}")
115
+ return {}, False
116
+
117
+ def exists_namespace(self, instance_id: str, namespace_name: str):
118
+ """
119
+ description:
120
+ 判断命名空间是否存在
121
+ parameters:
122
+ instance_id (str): 实例ID
123
+ namespace_name (str): 命名空间名称
124
+ return:
125
+ bool: 是否存在
126
+ """
127
+ response, success = self.list_namespaces(instance_id)
128
+ if not success:
129
+ return False, False
130
+ namespace_names = [
131
+ namespace["NamespaceName"] for namespace in response["Namespaces"]
132
+ ]
133
+ return namespace_name in namespace_names, True
134
+
135
+ def create_repository(
136
+ self,
137
+ instance_id: str,
138
+ namespace_name: str,
139
+ repository_name: str,
140
+ repository_type: str,
141
+ summary: str,
142
+ ):
143
+ """
144
+ description:
145
+ 创建仓库
146
+ parameters:
147
+ instance_id (str): 实例ID
148
+ namespace_name (str): 命名空间名称
149
+ repository_name (str): 仓库名称
150
+ repository_type (str): 仓库类型
151
+ summary (str): 仓库摘要
152
+ return:
153
+ dict: 创建结果
154
+ success(bool): 是否成功
155
+ """
156
+ if not re.fullmatch(r"[a-z\-]+", repository_name):
157
+ print("仓库名称只能包含小写英文字母和横杠")
158
+ return {}, False
159
+ try:
160
+ request = cr_20181201_models.CreateRepositoryRequest(
161
+ instance_id=instance_id,
162
+ repo_namespace_name=namespace_name,
163
+ repo_name=repository_name,
164
+ repo_type=repository_type,
165
+ summary=summary,
166
+ )
167
+ response = self.client.create_repository(request)
168
+ return response.body.to_map(), True
169
+ except Exception as e:
170
+ print(f"创建仓库失败: {e}")
171
+ return {}, False
172
+
173
+ def list_repositories(self, instance_id: str, namespace_name: str):
174
+ """
175
+ description:
176
+ 列出仓库
177
+ parameters:
178
+ instance_id (str): 实例ID
179
+ namespace_name (str): 命名空间名称
180
+ return:
181
+ dict: 包含仓库列表的响应数据
182
+ success(bool): 是否成功
183
+ """
184
+ try:
185
+ request = cr_20181201_models.ListRepositoryRequest(
186
+ instance_id=instance_id, repo_namespace_name=namespace_name
187
+ )
188
+ response = self.client.list_repository(request)
189
+ return response.body.to_map(), True
190
+ except Exception as e:
191
+ print(f"列出仓库失败: {e}")
192
+ return {}, False
193
+
194
+ def exists_repository(
195
+ self, instance_id: str, namespace_name: str, repository_name: str
196
+ ):
197
+ """
198
+ description:
199
+ 判断仓库是否存在
200
+ parameters:
201
+ instance_id (str): 实例ID
202
+ namespace_name (str): 命名空间名称
203
+ repository_name (str): 仓库名称
204
+ return:
205
+ bool: 是否存在
206
+ """
207
+ response, success = self.list_repositories(instance_id, namespace_name)
208
+ if not success:
209
+ return False, False
210
+ repository_names = [
211
+ repository["RepoName"] for repository in response["Repositories"]
212
+ ]
213
+ return repository_name in repository_names, True
214
+
215
+ def delete_repository(
216
+ self, instance_id: str, namespace_name: str, repository_id: str
217
+ ):
218
+ """
219
+ description:
220
+ 删除仓库
221
+ parameters:
222
+ instance_id (str): 实例ID
223
+ namespace_name (str): 命名空间名称
224
+ repository_id (str): 仓库ID
225
+ return:
226
+ dict: 删除结果
227
+ success(bool): 是否成功
228
+ """
229
+ try:
230
+ request = cr_20181201_models.DeleteRepositoryRequest(
231
+ instance_id=instance_id,
232
+ repo_namespace_name=namespace_name,
233
+ repo_id=repository_id,
234
+ )
235
+ response = self.client.delete_repository(request)
236
+ return response.body.to_map(), True
237
+ except Exception as e:
238
+ print(f"删除仓库失败: {e}")
239
+ return {}, False
240
+
241
+ def get_repository(
242
+ self, instance_id: str, namespace_name: str, repository_name: str
243
+ ):
244
+ """
245
+ description:
246
+ 获取仓库详情信息
247
+ parameters:
248
+ instance_id (str): 实例ID
249
+ namespace_name (str): 命名空间名称
250
+ repository_name (str): 仓库名称
251
+ return:
252
+ dict: 详情信息
253
+ success(bool): 是否成功
254
+ """
255
+ try:
256
+ request = cr_20181201_models.GetRepositoryRequest(
257
+ instance_id=instance_id,
258
+ repo_namespace_name=namespace_name,
259
+ repo_name=repository_name,
260
+ )
261
+ response = self.client.get_repository(request)
262
+ return response.body.to_map(), True
263
+ except Exception as e:
264
+ print(f"获取仓库详情失败: {e}")
265
+ return {}, False
266
+
267
+
268
+ class ACRUtilsAsync:
269
+ def __init__(self, region_id: str, access_key_id: str, access_key_secret: str):
270
+ self.region_id = region_id
271
+ self.access_key_id = access_key_id
272
+ self.access_key_secret = access_key_secret
273
+ self.client = self._create_client()
274
+
275
+ def _create_client(self):
276
+ credential = CredentialClient()
277
+ config = open_api_models.Config(
278
+ credential=credential,
279
+ access_key_id=self.access_key_id,
280
+ access_key_secret=self.access_key_secret,
281
+ )
282
+ # Endpoint 请参考 https://api.aliyun.com/product/cr
283
+ config.endpoint = f"cr.{self.region_id}.aliyuncs.com"
284
+ return cr20181201Client(config)
285
+
286
+ async def list_instances(self):
287
+ """
288
+ description:
289
+ 列出容器镜像服务实例
290
+ parameters:
291
+ None
292
+ return:
293
+ dict: 包含实例列表的响应数据
294
+ success(bool): 是否成功
295
+ """
296
+
297
+ try:
298
+ request = cr_20181201_models.ListInstanceRequest()
299
+ response = await self.client.list_instance_async(request)
300
+ return response.body.to_map(), True
301
+ except Exception as e:
302
+ print(f"列出实例失败: {e}")
303
+ return {}, False
304
+
305
+ async def create_namespace(self, instance_id: str, namespace_name: str):
306
+ """
307
+ description:
308
+ 创建命名空间
309
+ parameters:
310
+ instance_id (str): 实例ID
311
+ namespace_name (str): 命名空间名称
312
+ return:
313
+ dict: 创建结果
314
+ success(bool): 是否成功
315
+ """
316
+
317
+ try:
318
+ request = cr_20181201_models.CreateNamespaceRequest(
319
+ instance_id=instance_id, namespace_name=namespace_name
320
+ )
321
+ response = await self.client.create_namespace_async(request)
322
+ return response.body.to_map(), True
323
+ except Exception as e:
324
+ print(f"创建命名空间失败: {e}")
325
+ return {}, False
326
+
327
+ async def delete_namespace(self, instance_id: str, namespace_name: str):
328
+ """
329
+ description:
330
+ 删除命名空间
331
+ parameters:
332
+ instance_id (str): 实例ID
333
+ namespace_name (str): 命名空间名称
334
+ return:
335
+ dict: 删除结果
336
+ success(bool): 是否成功
337
+ """
338
+ try:
339
+ request = cr_20181201_models.DeleteNamespaceRequest(
340
+ instance_id=instance_id, namespace_name=namespace_name
341
+ )
342
+ response = await self.client.delete_namespace_async(request)
343
+ return response.body.to_map(), True
344
+ except Exception as e:
345
+ print(f"删除命名空间失败: {e}")
346
+ return {}, False
347
+
348
+ async def list_namespaces(self, instance_id: str):
349
+ """
350
+ description:
351
+ 列出命名空间
352
+ parameters:
353
+ instance_id (str): 实例ID
354
+ return:
355
+ dict: 包含命名空间列表的响应数据
356
+ success(bool): 是否成功
357
+ """
358
+ try:
359
+ request = cr_20181201_models.ListNamespaceRequest(instance_id=instance_id)
360
+ response = await self.client.list_namespace_async(request)
361
+ return response.body.to_map(), True
362
+ except Exception as e:
363
+ print(f"列出命名空间失败: {e}")
364
+ return {}, False
365
+
366
+ async def exists_namespace(self, instance_id: str, namespace_name: str):
367
+ """
368
+ description:
369
+ 判断命名空间是否存在
370
+ parameters:
371
+ instance_id (str): 实例ID
372
+ namespace_name (str): 命名空间名称
373
+ return:
374
+ bool: 是否存在
375
+ """
376
+ response, success = await self.list_namespaces(instance_id)
377
+ if not success:
378
+ return False, False
379
+ namespace_names = [
380
+ namespace["NamespaceName"] for namespace in response["Namespaces"]
381
+ ]
382
+ return namespace_name in namespace_names, True
383
+
384
+ async def create_repository(
385
+ self,
386
+ instance_id: str,
387
+ namespace_name: str,
388
+ repository_name: str,
389
+ repository_type: str,
390
+ summary: str,
391
+ ):
392
+ """
393
+ description:
394
+ 创建仓库
395
+ parameters:
396
+ instance_id (str): 实例ID
397
+ namespace_name (str): 命名空间名称
398
+ repository_name (str): 仓库名称
399
+ repository_type (str): 仓库类型
400
+ summary (str): 仓库摘要
401
+ return:
402
+ dict: 创建结果
403
+ success(bool): 是否成功
404
+ """
405
+ if not re.fullmatch(r"[a-z\-]+", repository_name):
406
+ print("仓库名称只能包含小写英文字母和横杠")
407
+ return {}, False
408
+ try:
409
+ request = cr_20181201_models.CreateRepositoryRequest(
410
+ instance_id=instance_id,
411
+ repo_namespace_name=namespace_name,
412
+ repo_name=repository_name,
413
+ repo_type=repository_type,
414
+ summary=summary,
415
+ )
416
+ response = await self.client.create_repository_async(request)
417
+ return response.body.to_map(), True
418
+ except Exception as e:
419
+ print(f"创建仓库失败: {e}")
420
+ return {}, False
421
+
422
+ async def list_repositories(self, instance_id: str, namespace_name: str):
423
+ """
424
+ description:
425
+ 列出仓库
426
+ parameters:
427
+ instance_id (str): 实例ID
428
+ namespace_name (str): 命名空间名称
429
+ return:
430
+ dict: 包含仓库列表的响应数据
431
+ success(bool): 是否成功
432
+ """
433
+ try:
434
+ request = cr_20181201_models.ListRepositoryRequest(
435
+ instance_id=instance_id, repo_namespace_name=namespace_name
436
+ )
437
+ response = await self.client.list_repository_async(request)
438
+ return response.body.to_map(), True
439
+ except Exception as e:
440
+ print(f"列出仓库失败: {e}")
441
+ return {}, False
442
+
443
+ async def exists_repository(
444
+ self, instance_id: str, namespace_name: str, repository_name: str
445
+ ):
446
+ """
447
+ description:
448
+ 判断仓库是否存在
449
+ parameters:
450
+ instance_id (str): 实例ID
451
+ namespace_name (str): 命名空间名称
452
+ repository_name (str): 仓库名称
453
+ return:
454
+ bool: 是否存在
455
+ """
456
+ response, success = await self.list_repositories(instance_id, namespace_name)
457
+ if not success:
458
+ return False, False
459
+ repository_names = [
460
+ repository["RepoName"] for repository in response["Repositories"]
461
+ ]
462
+ return repository_name in repository_names, True
463
+
464
+ async def delete_repository(
465
+ self, instance_id: str, namespace_name: str, repository_id: str
466
+ ):
467
+ """
468
+ description:
469
+ 删除仓库
470
+ parameters:
471
+ instance_id (str): 实例ID
472
+ namespace_name (str): 命名空间名称
473
+ repository_id (str): 仓库ID
474
+ return:
475
+ dict: 删除结果
476
+ success(bool): 是否成功
477
+ """
478
+ try:
479
+ request = cr_20181201_models.DeleteRepositoryRequest(
480
+ instance_id=instance_id,
481
+ repo_namespace_name=namespace_name,
482
+ repo_id=repository_id,
483
+ )
484
+ response = await self.client.delete_repository_async(request)
485
+ return response.body.to_map(), True
486
+ except Exception as e:
487
+ print(f"删除仓库失败: {e}")
488
+ return {}, False
489
+
490
+ async def get_repository(
491
+ self, instance_id: str, namespace_name: str, repository_name: str
492
+ ):
493
+ """
494
+ description:
495
+ 获取仓库详情信息
496
+ parameters:
497
+ instance_id (str): 实例ID
498
+ namespace_name (str): 命名空间名称
499
+ repository_name (str): 仓库名称
500
+ return:
501
+ dict: 详情信息
502
+ success(bool): 是否成功
503
+ """
504
+ try:
505
+ request = cr_20181201_models.GetRepositoryRequest(
506
+ instance_id=instance_id,
507
+ repo_namespace_name=namespace_name,
508
+ repo_name=repository_name,
509
+ )
510
+ response = await self.client.get_repository_async(request)
511
+ return response.body.to_map(), True
512
+ except Exception as e:
513
+ print(f"获取仓库详情失败: {e}")
514
+ return {}, False
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import os
2
3
  import time
3
4
  import random
@@ -122,9 +123,130 @@ class AliyunEmailSender:
122
123
  if response.status_code == 200:
123
124
  return True
124
125
  else:
125
- feishu_alert.send(f"发送验证邮件失败: {response}")
126
+ print(f"发送验证邮件失败: {response}")
126
127
  return False
127
128
 
128
129
  except Exception as e:
129
- feishu_alert.send(traceback.format_exc())
130
+ print(traceback.format_exc())
131
+ return False
132
+
133
+
134
+ class AliyunEmailSenderAsync:
135
+ def __init__(self, access_key_id, access_key_secret):
136
+ self.client = Dm20151123Client(
137
+ open_api_models.Config(
138
+ access_key_id=access_key_id,
139
+ access_key_secret=access_key_secret,
140
+ endpoint="dm.aliyuncs.com",
141
+ )
142
+ )
143
+
144
+ async def generate_verification_code(self, length=6):
145
+ """
146
+ description:
147
+ 生成数字验证码
148
+ parameters:
149
+ length(int): 验证码长度
150
+ return:
151
+ str: 验证码
152
+ """
153
+ return await asyncio.to_thread(
154
+ lambda: "".join(str(random.randint(0, 9)) for _ in range(length))
155
+ )
156
+
157
+ def create_verification_email_content(
158
+ self, username, verification_code, email_type, validity=15
159
+ ):
160
+ """
161
+ description:
162
+ 创建邮件HTML内容
163
+ parameters:
164
+ username(str): 用户名
165
+ validity(int): 验证码有效期
166
+ return:
167
+ str: 邮件HTML内容
168
+ """
169
+ email_type_str = "注册" if email_type == "register" else "找回密码"
170
+ return f"""
171
+ <html>
172
+ <head>
173
+ <style>
174
+ body {{ font-family: Arial, sans-serif; line-height: 1.6; }}
175
+ .container {{ max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e1e8f7; border-radius: 10px; }}
176
+ .header {{ background: linear-gradient(to right, #1a2980, #26d0ce); color: white; padding: 15px; text-align: center; border-radius: 8px; }}
177
+ .code {{ font-size: 28px; font-weight: bold; text-align: center; margin: 25px 0; letter-spacing: 5px; color: #e74c3c; }}
178
+ .footer {{ margin-top: 30px; text-align: center; color: #777; font-size: 12px; }}
179
+ </style>
180
+ </head>
181
+ <body>
182
+ <div class="container">
183
+ <div class="header">
184
+ <h2>北京矩阵像素科技有限公司</h2>
185
+ </div>
186
+
187
+ <p>尊敬的 <strong>{username}</strong>:</p>
188
+ <p>您正在进行邮箱{email_type_str}验证,您的验证码为:</p>
189
+ <div class="code">{verification_code}</div>
190
+ <p>该验证码 <strong>{validity}分钟</strong> 内有效,请尽快完成验证。</p>
191
+ <p>如非本人操作,请忽略此邮件。</p>
192
+
193
+ <div class="footer">
194
+ <p>阿里云邮件推送服务 | 安全验证</p>
195
+ <p>© {time.strftime('%Y')} 企业注册系统 版权所有</p>
196
+ </div>
197
+ </div>
198
+ </body>
199
+ </html>
200
+ """
201
+
202
+ def get_email_subject(self, email_type):
203
+ if email_type == "register":
204
+ return "您的注册验证码"
205
+ elif email_type == "forgot":
206
+ return "您的找回密码验证码"
207
+ else:
208
+ return "您的验证码"
209
+
210
+ async def send_verification_email(
211
+ self, to_address, verification_code, email_type, username="用户"
212
+ ):
213
+ """
214
+ description:
215
+ 发送验证邮件
216
+ parameters:
217
+ to_address(str): 收件人邮箱地址
218
+ email_type(str): 邮件类型
219
+ username(str): 用户名
220
+ return:
221
+ dict: 发送结果
222
+ """
223
+ assert email_type in [
224
+ "register",
225
+ "forgot",
226
+ ], "邮件类型错误,当前只有register和forgot两种类型"
227
+ try:
228
+ response = await self.client.single_send_mail_with_options_async(
229
+ dm_20151123_models.SingleSendMailRequest(
230
+ account_name="captcha_new@captcha.pixelarrayai.com",
231
+ address_type=1,
232
+ to_address=to_address,
233
+ subject=self.get_email_subject(email_type),
234
+ html_body=self.create_verification_email_content(
235
+ username=username,
236
+ verification_code=verification_code,
237
+ email_type=email_type,
238
+ ),
239
+ reply_to_address=False,
240
+ from_alias="系统验证", # 发信人昵称
241
+ ),
242
+ util_models.RuntimeOptions(),
243
+ )
244
+ if response.status_code == 200:
245
+ return True
246
+ else:
247
+ print(f"发送验证邮件失败: {response}")
248
+ return False
249
+
250
+ except Exception as e:
251
+ print(traceback.format_exc())
130
252
  return False
@@ -244,7 +244,7 @@ class BillingUtils:
244
244
  return formatted_response
245
245
 
246
246
  except Exception as e:
247
- feishu_alert.send(traceback.format_exc())
247
+ print(traceback.format_exc())
248
248
  error_response = {
249
249
  "Message": add_comment(f"Format error: {str(e)}", "Message"),
250
250
  "RequestId": add_comment(
@@ -326,7 +326,7 @@ class BillingUtils:
326
326
  return formatted_response
327
327
 
328
328
  except Exception as error:
329
- feishu_alert.send(traceback.format_exc())
329
+ print(traceback.format_exc())
330
330
  error_msg = getattr(error, "message", str(error))
331
331
  # 诊断地址
332
332
  diagnose_url = ""
@@ -473,5 +473,5 @@ class BillingUtils:
473
473
  return output_path
474
474
 
475
475
  except Exception as e:
476
- feishu_alert.send(traceback.format_exc())
476
+ print(traceback.format_exc())
477
477
  raise Exception(f"保存账单数据失败: {str(e)}")