pixelarraylib 1.1.7__tar.gz → 1.1.8__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 (50) hide show
  1. {pixelarraylib-1.1.7/pixelarraylib.egg-info → pixelarraylib-1.1.8}/PKG-INFO +1 -1
  2. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/__init__.py +1 -1
  3. pixelarraylib-1.1.8/pixelarraylib/monitor/feishu.py +573 -0
  4. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/__init__.py +1 -1
  5. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8/pixelarraylib.egg-info}/PKG-INFO +1 -1
  6. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pyproject.toml +1 -1
  7. pixelarraylib-1.1.7/pixelarraylib/monitor/feishu.py +0 -275
  8. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/LICENSE +0 -0
  9. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/MANIFEST.in +0 -0
  10. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/README.md +0 -0
  11. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/__main__.py +0 -0
  12. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/__init__.py +0 -0
  13. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/acr.py +0 -0
  14. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/aliyun_email.py +0 -0
  15. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/billing.py +0 -0
  16. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/content_scanner.py +0 -0
  17. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/domain.py +0 -0
  18. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/eci.py +0 -0
  19. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/ecs.py +0 -0
  20. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/eip.py +0 -0
  21. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/fc.py +0 -0
  22. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/oss.py +0 -0
  23. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/sms.py +0 -0
  24. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/aliyun/sts.py +0 -0
  25. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/db_utils/mysql.py +0 -0
  26. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/db_utils/redis.py +0 -0
  27. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/decorators/__init__.py +0 -0
  28. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/decorators/decorators.py +0 -0
  29. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/gitlab/__init__.py +0 -0
  30. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/gitlab/code_analyzer.py +0 -0
  31. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/gitlab/pypi_package_manager.py +0 -0
  32. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/monitor/__init__.py +0 -0
  33. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/build_website.py +0 -0
  34. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/collect_code_to_txt.py +0 -0
  35. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/create_test_case_files.py +0 -0
  36. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/nginx_proxy_to_ecs.py +0 -0
  37. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/remove_empty_lines.py +0 -0
  38. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/scripts/tson_convert.py +0 -0
  39. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/system/__init__.py +0 -0
  40. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/system/common.py +0 -0
  41. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/system/cron_manager.py +0 -0
  42. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/system/tson.py +0 -0
  43. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib/utils/name_generator.py +0 -0
  44. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib.egg-info/SOURCES.txt +0 -0
  45. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib.egg-info/dependency_links.txt +0 -0
  46. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib.egg-info/entry_points.txt +0 -0
  47. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib.egg-info/requires.txt +0 -0
  48. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/pixelarraylib.egg-info/top_level.txt +0 -0
  49. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/requirements.txt +0 -0
  50. {pixelarraylib-1.1.7 → pixelarraylib-1.1.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraylib
3
- Version: 1.1.7
3
+ Version: 1.1.8
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.1.7"
23
+ __version__ = "1.1.8"
24
24
  __author__ = "PixelArray"
25
25
  __email__ = "qi.lu@pixelarrayai.com"
26
26
 
@@ -0,0 +1,573 @@
1
+ import requests
2
+ import json
3
+ import asyncio
4
+ import csv
5
+ import os
6
+ from typing import Optional
7
+
8
+
9
+ class Feishu:
10
+ channel_map = {
11
+ "矩阵像素订阅群": "https://open.feishu.cn/open-apis/bot/v2/hook/6e368741-ab2e-46f4-a945-5c1182303f91",
12
+ "devtoolkit服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/b9e7cfa1-c63f-4a9f-9699-286f7316784b",
13
+ "baymax服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5d8d4fa6-67c4-4202-9122-389d1ec2b668",
14
+ "videodriver服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5359b92d-02ab-47ca-a617-58b8152eaa2d",
15
+ "arraycut服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/e610b1e8-f867-4670-8d4d-0553f64884f0",
16
+ "knowledgebase微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5f007914-235a-4287-8349-6c1dd7c70024",
17
+ "llm微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/54942aa6-24f1-4851-8fe9-d7c87572d00a",
18
+ "thirdparty微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/b1e6237a-1323-4ad9-96f4-d74de5cdc00f",
19
+ "picturebed服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/d3c2e68c-3ed3-4832-9b66-76db5bd69b42",
20
+ "picturetransform服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/e975aa0a-acef-4e3f-bee4-6dc507b87ebd",
21
+ "cloudstorage服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/a632d0bc-e400-40ce-a3bb-1b9b9ee634f4",
22
+ "deployengine服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/9a347a63-58fb-4e10-9a3d-ff4918ddb3a9",
23
+ }
24
+
25
+ def __init__(
26
+ self,
27
+ channel_name,
28
+ app_id: Optional[str] = None,
29
+ app_secret: Optional[str] = None,
30
+ ):
31
+ """
32
+ description:
33
+ 初始化飞书告警客户端
34
+ parameters:
35
+ channel_name(str): 飞书频道名称
36
+ app_id(Optional[str]): 应用ID,可选
37
+ app_secret(Optional[str]): 应用密钥,可选
38
+ """
39
+ self.webhook_url = self.channel_map[channel_name]
40
+ self.app_id = app_id
41
+ self.app_secret = app_secret
42
+ self._access_token = None
43
+
44
+ def send(self, text):
45
+ """
46
+ description:
47
+ 发送文本消息到飞书群
48
+ parameters:
49
+ text(str): 要发送的文本内容
50
+ return:
51
+ success(bool): 发送是否成功
52
+ """
53
+ print(text)
54
+ headers = {"Content-Type": "application/json"}
55
+ data = {"msg_type": "text", "content": {"text": text}}
56
+ response = requests.post(
57
+ self.webhook_url, headers=headers, data=json.dumps(data)
58
+ )
59
+ return bool(
60
+ response
61
+ and response.json().get("StatusCode") == 0
62
+ and response.json().get("StatusMessage") == "success"
63
+ )
64
+
65
+ async def send_async(self, text: str):
66
+ """
67
+ description:
68
+ 异步发送文本消息到飞书群
69
+ parameters:
70
+ text(str): 要发送的文本内容
71
+ return:
72
+ success(bool): 发送是否成功
73
+ """
74
+ return await asyncio.to_thread(self.send, text)
75
+
76
+ def send_markdown(
77
+ self,
78
+ markdown_content: str,
79
+ title: str,
80
+ template: str = "turquoise",
81
+ ):
82
+ """
83
+ description:
84
+ 发送Markdown格式的消息到飞书群
85
+ parameters:
86
+ markdown_content(str): Markdown格式的内容
87
+ title(str): 消息标题
88
+ template(str): 卡片模板颜色,默认为"turquoise"
89
+ return:
90
+ success(bool): 发送是否成功
91
+ """
92
+ headers = {"Content-Type": "application/json; charset=utf-8"}
93
+ card = {
94
+ "config": {"wide_screen_mode": True, "enable_forward": True},
95
+ "header": {
96
+ "title": {"tag": "plain_text", "content": title},
97
+ "template": template,
98
+ },
99
+ "elements": [
100
+ {"tag": "div", "text": {"tag": "lark_md", "content": markdown_content}}
101
+ ],
102
+ }
103
+ payload = {"msg_type": "interactive", "card": card}
104
+ resp = requests.post(
105
+ self.webhook_url, headers=headers, data=json.dumps(payload)
106
+ )
107
+ return bool(resp.ok and resp.json().get("StatusCode") == 0)
108
+
109
+ async def send_markdown_async(
110
+ self,
111
+ markdown_content: str,
112
+ title: str,
113
+ template: str = "turquoise",
114
+ ):
115
+ """
116
+ description:
117
+ 异步发送Markdown格式的消息到飞书群
118
+ parameters:
119
+ markdown_content(str): Markdown格式的内容
120
+ title(str): 消息标题
121
+ template(str): 卡片模板颜色,默认为"turquoise"
122
+ return:
123
+ success(bool): 发送是否成功
124
+ """
125
+ return await asyncio.to_thread(
126
+ self.send_markdown, markdown_content, title, template
127
+ )
128
+
129
+ def send_table(
130
+ self,
131
+ headers: list,
132
+ rows: list,
133
+ title: str,
134
+ template: str = "turquoise",
135
+ ):
136
+ """
137
+ description:
138
+ 发送表格消息到飞书群
139
+ parameters:
140
+ headers(list): 表头列表
141
+ rows(list): 表格行数据列表
142
+ title(str): 消息标题
143
+ template(str): 卡片模板颜色,默认为"turquoise"
144
+ return:
145
+ success(bool): 发送是否成功
146
+ """
147
+ headers_req = {"Content-Type": "application/json; charset=utf-8"}
148
+ n_cols = len(headers)
149
+ columns = []
150
+ for ci in range(n_cols):
151
+ elements_in_col = []
152
+ # 表头
153
+ elements_in_col.append(
154
+ {
155
+ "tag": "div",
156
+ "text": {"tag": "lark_md", "content": f"**{headers[ci]}**"},
157
+ }
158
+ )
159
+ # 每一行的该列内容
160
+ for row in rows:
161
+ cell = row[ci] if ci < len(row) else ""
162
+ elements_in_col.append(
163
+ {"tag": "div", "text": {"tag": "lark_md", "content": cell}}
164
+ )
165
+ columns.append(
166
+ {
167
+ "tag": "column",
168
+ "width": "weighted",
169
+ "weight": 1,
170
+ "elements": elements_in_col,
171
+ }
172
+ )
173
+
174
+ elements = [{"tag": "column_set", "columns": columns}]
175
+
176
+ card = {
177
+ "config": {"wide_screen_mode": True, "enable_forward": True},
178
+ "header": {
179
+ "title": {"tag": "plain_text", "content": title},
180
+ "template": template,
181
+ },
182
+ "elements": elements,
183
+ }
184
+ payload = {"msg_type": "interactive", "card": card}
185
+
186
+ resp = requests.post(
187
+ self.webhook_url, headers=headers_req, data=json.dumps(payload)
188
+ )
189
+ try:
190
+ print("feishu send_table resp:", resp.status_code, resp.json())
191
+ except Exception:
192
+ print("feishu send_table resp:", resp.status_code, resp.text)
193
+ return bool(resp.ok and resp.json().get("StatusCode") == 0)
194
+
195
+ async def send_table_async(
196
+ self,
197
+ headers: list[str],
198
+ rows: list[list[str]],
199
+ title: str,
200
+ template: str = "turquoise",
201
+ ):
202
+ """
203
+ description:
204
+ 异步发送表格消息到飞书群
205
+ parameters:
206
+ headers(list[str]): 表头列表
207
+ rows(list[list[str]]): 表格行数据列表
208
+ title(str): 消息标题
209
+ template(str): 卡片模板颜色,默认为"turquoise"
210
+ return:
211
+ success(bool): 发送是否成功
212
+ """
213
+ return await asyncio.to_thread(self.send_table, headers, rows, title, template)
214
+
215
+ def send_file(
216
+ self,
217
+ file_url: Optional[str] = None,
218
+ title: Optional[str] = None,
219
+ template: str = "turquoise",
220
+ ):
221
+ """
222
+ description:
223
+ 发送文件到飞书群
224
+ 通过文件下载链接发送包含文件下载按钮的卡片消息
225
+ parameters:
226
+ file_url(str): 文件下载链接(如 OSS 链接),必需参数
227
+ title(str): 消息标题,如果为 None 则使用文件名
228
+ template(str): 卡片模板颜色,默认 "turquoise"
229
+ return:
230
+ bool: 发送是否成功
231
+ """
232
+ if not file_url:
233
+ print("错误:必须提供 file_url(文件下载链接)")
234
+ return False
235
+ headers = {"Content-Type": "application/json; charset=utf-8"}
236
+ markdown_content = f"[点击下载文件]({file_url})"
237
+
238
+ card = {
239
+ "config": {"wide_screen_mode": True, "enable_forward": True},
240
+ "header": {
241
+ "title": {"tag": "plain_text", "content": title},
242
+ "template": template,
243
+ },
244
+ "elements": [
245
+ {"tag": "div", "text": {"tag": "lark_md", "content": markdown_content}}
246
+ ],
247
+ }
248
+ payload = {"msg_type": "interactive", "card": card}
249
+
250
+ try:
251
+ resp = requests.post(
252
+ self.webhook_url, headers=headers, data=json.dumps(payload)
253
+ )
254
+ if resp.ok:
255
+ result = resp.json()
256
+ return bool(result.get("StatusCode") == 0 or result.get("code") == 0)
257
+ else:
258
+ print(f"发送文件消息失败: {resp.status_code}, {resp.text}")
259
+ return False
260
+ except Exception as e:
261
+ print(f"发送文件消息异常: {e}")
262
+ return False
263
+
264
+ async def send_file_async(self, file_url: str, title: str, template: str):
265
+ """
266
+ description:
267
+ 异步发送文件到飞书群
268
+ parameters:
269
+ file_url(str): 文件下载链接
270
+ title(str): 消息标题
271
+ template(str): 卡片模板颜色
272
+ return:
273
+ success(bool): 发送是否成功
274
+ """
275
+ return await asyncio.to_thread(self.send_file, file_url, title, template)
276
+
277
+
278
+ class FeishuDocumentManager:
279
+ """
280
+ 飞书文档管理器,用于管理飞书文档的创建、查询、更新和删除
281
+ """
282
+
283
+ def __init__(
284
+ self,
285
+ app_id: Optional[str] = None,
286
+ app_secret: Optional[str] = None,
287
+ ):
288
+ """
289
+ description:
290
+ 初始化飞书文档管理器
291
+ parameters:
292
+ app_id(Optional[str]): 飞书应用ID,如果不提供则从settings中读取
293
+ app_secret(Optional[str]): 飞书应用密钥,如果不提供则从settings中读取
294
+ """
295
+ try:
296
+ from settings import FEISHU_APP_ID, FEISHU_APP_SECRET
297
+ self.app_id = app_id or FEISHU_APP_ID
298
+ self.app_secret = app_secret or FEISHU_APP_SECRET
299
+ except ImportError:
300
+ if not app_id or not app_secret:
301
+ raise ValueError(
302
+ "必须提供app_id和app_secret,或者在settings中配置FEISHU_APP_ID和FEISHU_APP_SECRET"
303
+ )
304
+ self.app_id = app_id
305
+ self.app_secret = app_secret
306
+
307
+ self.base_url = "https://open.feishu.cn/open-apis"
308
+ self._access_token = None
309
+ self._token_expire_time = 0
310
+
311
+ def _get_access_token(self) -> str:
312
+ """
313
+ description:
314
+ 获取飞书应用的访问令牌(带缓存机制)
315
+ return:
316
+ str: 访问令牌
317
+ """
318
+ import time
319
+
320
+ # 如果token未过期,直接返回缓存的token
321
+ if self._access_token and time.time() < self._token_expire_time:
322
+ return self._access_token
323
+
324
+ url = f"{self.base_url}/auth/v3/app_access_token/internal"
325
+ headers = {"Content-Type": "application/json; charset=utf-8"}
326
+ data = {
327
+ "app_id": self.app_id,
328
+ "app_secret": self.app_secret,
329
+ }
330
+
331
+ try:
332
+ response = requests.post(url, headers=headers, json=data)
333
+ response.raise_for_status()
334
+ result = response.json()
335
+
336
+ if result.get("code") != 0:
337
+ raise Exception(
338
+ f"获取access_token失败: {result.get('msg', 'unknown error')}"
339
+ )
340
+
341
+ self._access_token = result.get("app_access_token")
342
+ # token有效期默认7200秒,提前300秒刷新
343
+ expire = result.get("expire", 7200)
344
+ self._token_expire_time = time.time() + expire - 300
345
+
346
+ return self._access_token
347
+ except requests.RequestException as e:
348
+ raise Exception(f"请求access_token时发生错误: {str(e)}")
349
+
350
+ def _get_headers(self) -> dict:
351
+ """
352
+ description:
353
+ 获取请求头,包含access_token
354
+ return:
355
+ dict: 请求头字典
356
+ """
357
+ return {
358
+ "Authorization": f"Bearer {self._get_access_token()}",
359
+ "Content-Type": "application/json; charset=utf-8",
360
+ }
361
+
362
+ def list_documents(
363
+ self,
364
+ user_id: Optional[str] = None,
365
+ folder_token: Optional[str] = None,
366
+ page_size: int = 50,
367
+ page_token: Optional[str] = None,
368
+ order_by: str = "EditedTime",
369
+ direction: str = "DESC",
370
+ ) -> tuple[dict, bool]:
371
+ """
372
+ description:
373
+ 列出文档列表(支持按用户或文件夹筛选)
374
+ parameters:
375
+ user_id(Optional[str]): 用户ID,如果提供则只返回该用户的文档
376
+ folder_token(Optional[str]): 文件夹token,如果提供则只返回该文件夹下的文档
377
+ page_size(int): 每页数量,默认50
378
+ page_token(Optional[str]): 分页token,用于获取下一页
379
+ order_by(str): 排序字段,默认为"EditedTime"(编辑时间)
380
+ direction(str): 排序方向,"ASC"或"DESC",默认为"DESC"
381
+ return:
382
+ result(dict): API返回的结果,包含文档列表
383
+ success(bool): 是否成功
384
+ """
385
+ url = f"{self.base_url}/drive/v1/files"
386
+ headers = self._get_headers()
387
+ params = {
388
+ "page_size": page_size,
389
+ "order_by": order_by,
390
+ "direction": direction,
391
+ }
392
+
393
+ if user_id:
394
+ params["user_id"] = user_id
395
+ if folder_token:
396
+ params["folder_token"] = folder_token
397
+ if page_token:
398
+ params["page_token"] = page_token
399
+
400
+ try:
401
+ response = requests.get(url, headers=headers, params=params)
402
+ response.raise_for_status()
403
+ result = response.json()
404
+
405
+ if result.get("code") != 0:
406
+ return result, False
407
+
408
+ return result, True
409
+ except requests.RequestException as e:
410
+ return {"code": -1, "msg": f"请求失败: {str(e)}"}, False
411
+
412
+ def create_document(
413
+ self,
414
+ folder_token: str,
415
+ title: str,
416
+ doc_type: str = "doc",
417
+ ) -> tuple[dict, bool]:
418
+ """
419
+ description:
420
+ 创建新文档
421
+ parameters:
422
+ folder_token(str): 目标文件夹token
423
+ title(str): 文档标题
424
+ doc_type(str): 文档类型,"doc"表示文档,"sheet"表示电子表格,"bitable"表示多维表格,默认为"doc"
425
+ return:
426
+ result(dict): API返回的结果,包含创建的文档信息
427
+ success(bool): 是否成功
428
+ """
429
+ url = f"{self.base_url}/drive/v1/files/create"
430
+ headers = self._get_headers()
431
+ data = {
432
+ "folder_token": folder_token,
433
+ "title": title,
434
+ "type": doc_type,
435
+ }
436
+
437
+ try:
438
+ response = requests.post(url, headers=headers, json=data)
439
+ response.raise_for_status()
440
+ result = response.json()
441
+
442
+ if result.get("code") != 0:
443
+ return result, False
444
+
445
+ return result, True
446
+ except requests.RequestException as e:
447
+ return {"code": -1, "msg": f"请求失败: {str(e)}"}, False
448
+
449
+ def update_document(
450
+ self,
451
+ document_id: str,
452
+ title: Optional[str] = None,
453
+ content: Optional[str] = None,
454
+ ) -> tuple[dict, bool]:
455
+ """
456
+ description:
457
+ 更新文档(支持更新标题和内容)
458
+ parameters:
459
+ document_id(str): 文档ID(doc_token)
460
+ title(Optional[str]): 新的文档标题,如果提供则更新标题
461
+ content(Optional[str]): 新的文档内容(Markdown格式),如果提供则更新内容
462
+ return:
463
+ result(dict): API返回的结果
464
+ success(bool): 是否成功
465
+ """
466
+ success_count = 0
467
+
468
+ # 更新标题
469
+ if title:
470
+ url = f"{self.base_url}/drive/v1/files/{document_id}"
471
+ headers = self._get_headers()
472
+ data = {"name": title}
473
+
474
+ try:
475
+ response = requests.patch(url, headers=headers, json=data)
476
+ response.raise_for_status()
477
+ result = response.json()
478
+
479
+ if result.get("code") == 0:
480
+ success_count += 1
481
+ else:
482
+ return result, False
483
+ except requests.RequestException as e:
484
+ return {"code": -1, "msg": f"更新标题失败: {str(e)}"}, False
485
+
486
+ # 更新内容(使用docx API)
487
+ if content:
488
+ url = f"{self.base_url}/docx/v1/documents/{document_id}/blocks/{document_id}"
489
+ headers = self._get_headers()
490
+ # 构建文档块内容
491
+ blocks = [
492
+ {
493
+ "block_id": document_id,
494
+ "block_type": 1, # 1表示文本块
495
+ "text": {
496
+ "elements": [
497
+ {
498
+ "text_run": {
499
+ "content": content,
500
+ }
501
+ }
502
+ ]
503
+ },
504
+ }
505
+ ]
506
+ data = {"document_revision_id": -1, "blocks": blocks}
507
+
508
+ try:
509
+ response = requests.patch(url, headers=headers, json=data)
510
+ response.raise_for_status()
511
+ result = response.json()
512
+
513
+ if result.get("code") == 0:
514
+ success_count += 1
515
+ else:
516
+ return result, False
517
+ except requests.RequestException as e:
518
+ return {"code": -1, "msg": f"更新内容失败: {str(e)}"}, False
519
+
520
+ if success_count > 0:
521
+ return {"code": 0, "msg": "success"}, True
522
+ else:
523
+ return {"code": -1, "msg": "没有提供需要更新的内容"}, False
524
+
525
+ def delete_document(self, file_token: str) -> tuple[dict, bool]:
526
+ """
527
+ description:
528
+ 删除文档
529
+ parameters:
530
+ file_token(str): 文件token(文档ID)
531
+ return:
532
+ result(dict): API返回的结果
533
+ success(bool): 是否成功
534
+ """
535
+ url = f"{self.base_url}/drive/v1/files/{file_token}"
536
+ headers = self._get_headers()
537
+
538
+ try:
539
+ response = requests.delete(url, headers=headers)
540
+ response.raise_for_status()
541
+ result = response.json()
542
+
543
+ if result.get("code") != 0:
544
+ return result, False
545
+
546
+ return result, True
547
+ except requests.RequestException as e:
548
+ return {"code": -1, "msg": f"请求失败: {str(e)}"}, False
549
+
550
+ def get_document_info(self, document_id: str) -> tuple[dict, bool]:
551
+ """
552
+ description:
553
+ 获取文档详细信息
554
+ parameters:
555
+ document_id(str): 文档ID(file_token)
556
+ return:
557
+ result(dict): API返回的结果,包含文档信息
558
+ success(bool): 是否成功
559
+ """
560
+ url = f"{self.base_url}/drive/v1/files/{document_id}"
561
+ headers = self._get_headers()
562
+
563
+ try:
564
+ response = requests.get(url, headers=headers)
565
+ response.raise_for_status()
566
+ result = response.json()
567
+
568
+ if result.get("code") != 0:
569
+ return result, False
570
+
571
+ return result, True
572
+ except requests.RequestException as e:
573
+ return {"code": -1, "msg": f"请求失败: {str(e)}"}, False
@@ -3,7 +3,7 @@ PixelArrayLib 脚本工具包
3
3
  包含各种实用的命令行工具和脚本
4
4
  """
5
5
 
6
- __version__ = "1.1.7"
6
+ __version__ = "1.1.8"
7
7
  __author__ = "Lu qi"
8
8
 
9
9
  # 导出主要的脚本函数
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraylib
3
- Version: 1.1.7
3
+ Version: 1.1.8
4
4
  Summary: PixelArray Python开发工具库 - 包含阿里云服务、数据库工具、装饰器、监控等功能
5
5
  Author-email: Lu qi <qi.lu@pixelarrayai.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pixelarraylib"
7
- version = "1.1.7"
7
+ version = "1.1.8"
8
8
  authors = [
9
9
  {name = "Lu qi", email = "qi.lu@pixelarrayai.com"},
10
10
  ]
@@ -1,275 +0,0 @@
1
- import requests
2
- import json
3
- import asyncio
4
- import csv
5
- import os
6
- from typing import Optional
7
-
8
-
9
- class Feishu:
10
- channel_map = {
11
- "矩阵像素订阅群": "https://open.feishu.cn/open-apis/bot/v2/hook/6e368741-ab2e-46f4-a945-5c1182303f91",
12
- "devtoolkit服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/b9e7cfa1-c63f-4a9f-9699-286f7316784b",
13
- "baymax服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5d8d4fa6-67c4-4202-9122-389d1ec2b668",
14
- "videodriver服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5359b92d-02ab-47ca-a617-58b8152eaa2d",
15
- "arraycut服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/e610b1e8-f867-4670-8d4d-0553f64884f0",
16
- "knowledgebase微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/5f007914-235a-4287-8349-6c1dd7c70024",
17
- "llm微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/54942aa6-24f1-4851-8fe9-d7c87572d00a",
18
- "thirdparty微服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/b1e6237a-1323-4ad9-96f4-d74de5cdc00f",
19
- "picturebed服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/d3c2e68c-3ed3-4832-9b66-76db5bd69b42",
20
- "picturetransform服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/e975aa0a-acef-4e3f-bee4-6dc507b87ebd",
21
- "cloudstorage服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/a632d0bc-e400-40ce-a3bb-1b9b9ee634f4",
22
- "deployengine服务报警": "https://open.feishu.cn/open-apis/bot/v2/hook/9a347a63-58fb-4e10-9a3d-ff4918ddb3a9",
23
- }
24
-
25
- def __init__(
26
- self,
27
- channel_name,
28
- app_id: Optional[str] = None,
29
- app_secret: Optional[str] = None,
30
- ):
31
- """
32
- description:
33
- 初始化飞书告警客户端
34
- parameters:
35
- channel_name(str): 飞书频道名称
36
- app_id(Optional[str]): 应用ID,可选
37
- app_secret(Optional[str]): 应用密钥,可选
38
- """
39
- self.webhook_url = self.channel_map[channel_name]
40
- self.app_id = app_id
41
- self.app_secret = app_secret
42
- self._access_token = None
43
-
44
- def send(self, text):
45
- """
46
- description:
47
- 发送文本消息到飞书群
48
- parameters:
49
- text(str): 要发送的文本内容
50
- return:
51
- success(bool): 发送是否成功
52
- """
53
- print(text)
54
- headers = {"Content-Type": "application/json"}
55
- data = {"msg_type": "text", "content": {"text": text}}
56
- response = requests.post(
57
- self.webhook_url, headers=headers, data=json.dumps(data)
58
- )
59
- return bool(
60
- response
61
- and response.json().get("StatusCode") == 0
62
- and response.json().get("StatusMessage") == "success"
63
- )
64
-
65
- async def send_async(self, text: str):
66
- """
67
- description:
68
- 异步发送文本消息到飞书群
69
- parameters:
70
- text(str): 要发送的文本内容
71
- return:
72
- success(bool): 发送是否成功
73
- """
74
- return await asyncio.to_thread(self.send, text)
75
-
76
- def send_markdown(
77
- self,
78
- markdown_content: str,
79
- title: str,
80
- template: str = "turquoise",
81
- ):
82
- """
83
- description:
84
- 发送Markdown格式的消息到飞书群
85
- parameters:
86
- markdown_content(str): Markdown格式的内容
87
- title(str): 消息标题
88
- template(str): 卡片模板颜色,默认为"turquoise"
89
- return:
90
- success(bool): 发送是否成功
91
- """
92
- headers = {"Content-Type": "application/json; charset=utf-8"}
93
- card = {
94
- "config": {"wide_screen_mode": True, "enable_forward": True},
95
- "header": {
96
- "title": {"tag": "plain_text", "content": title},
97
- "template": template,
98
- },
99
- "elements": [
100
- {"tag": "div", "text": {"tag": "lark_md", "content": markdown_content}}
101
- ],
102
- }
103
- payload = {"msg_type": "interactive", "card": card}
104
- resp = requests.post(
105
- self.webhook_url, headers=headers, data=json.dumps(payload)
106
- )
107
- return bool(resp.ok and resp.json().get("StatusCode") == 0)
108
-
109
- async def send_markdown_async(
110
- self,
111
- markdown_content: str,
112
- title: str,
113
- template: str = "turquoise",
114
- ):
115
- """
116
- description:
117
- 异步发送Markdown格式的消息到飞书群
118
- parameters:
119
- markdown_content(str): Markdown格式的内容
120
- title(str): 消息标题
121
- template(str): 卡片模板颜色,默认为"turquoise"
122
- return:
123
- success(bool): 发送是否成功
124
- """
125
- return await asyncio.to_thread(
126
- self.send_markdown, markdown_content, title, template
127
- )
128
-
129
- def send_table(
130
- self,
131
- headers: list,
132
- rows: list,
133
- title: str,
134
- template: str = "turquoise",
135
- ):
136
- """
137
- description:
138
- 发送表格消息到飞书群
139
- parameters:
140
- headers(list): 表头列表
141
- rows(list): 表格行数据列表
142
- title(str): 消息标题
143
- template(str): 卡片模板颜色,默认为"turquoise"
144
- return:
145
- success(bool): 发送是否成功
146
- """
147
- headers_req = {"Content-Type": "application/json; charset=utf-8"}
148
- n_cols = len(headers)
149
- columns = []
150
- for ci in range(n_cols):
151
- elements_in_col = []
152
- # 表头
153
- elements_in_col.append(
154
- {
155
- "tag": "div",
156
- "text": {"tag": "lark_md", "content": f"**{headers[ci]}**"},
157
- }
158
- )
159
- # 每一行的该列内容
160
- for row in rows:
161
- cell = row[ci] if ci < len(row) else ""
162
- elements_in_col.append(
163
- {"tag": "div", "text": {"tag": "lark_md", "content": cell}}
164
- )
165
- columns.append(
166
- {
167
- "tag": "column",
168
- "width": "weighted",
169
- "weight": 1,
170
- "elements": elements_in_col,
171
- }
172
- )
173
-
174
- elements = [{"tag": "column_set", "columns": columns}]
175
-
176
- card = {
177
- "config": {"wide_screen_mode": True, "enable_forward": True},
178
- "header": {
179
- "title": {"tag": "plain_text", "content": title},
180
- "template": template,
181
- },
182
- "elements": elements,
183
- }
184
- payload = {"msg_type": "interactive", "card": card}
185
-
186
- resp = requests.post(
187
- self.webhook_url, headers=headers_req, data=json.dumps(payload)
188
- )
189
- try:
190
- print("feishu send_table resp:", resp.status_code, resp.json())
191
- except Exception:
192
- print("feishu send_table resp:", resp.status_code, resp.text)
193
- return bool(resp.ok and resp.json().get("StatusCode") == 0)
194
-
195
- async def send_table_async(
196
- self,
197
- headers: list[str],
198
- rows: list[list[str]],
199
- title: str,
200
- template: str = "turquoise",
201
- ):
202
- """
203
- description:
204
- 异步发送表格消息到飞书群
205
- parameters:
206
- headers(list[str]): 表头列表
207
- rows(list[list[str]]): 表格行数据列表
208
- title(str): 消息标题
209
- template(str): 卡片模板颜色,默认为"turquoise"
210
- return:
211
- success(bool): 发送是否成功
212
- """
213
- return await asyncio.to_thread(self.send_table, headers, rows, title, template)
214
-
215
- def send_file(
216
- self,
217
- file_url: Optional[str] = None,
218
- title: Optional[str] = None,
219
- template: str = "turquoise",
220
- ):
221
- """
222
- description:
223
- 发送文件到飞书群
224
- 通过文件下载链接发送包含文件下载按钮的卡片消息
225
- parameters:
226
- file_url(str): 文件下载链接(如 OSS 链接),必需参数
227
- title(str): 消息标题,如果为 None 则使用文件名
228
- template(str): 卡片模板颜色,默认 "turquoise"
229
- return:
230
- bool: 发送是否成功
231
- """
232
- if not file_url:
233
- print("错误:必须提供 file_url(文件下载链接)")
234
- return False
235
- headers = {"Content-Type": "application/json; charset=utf-8"}
236
- markdown_content = f"[点击下载文件]({file_url})"
237
-
238
- card = {
239
- "config": {"wide_screen_mode": True, "enable_forward": True},
240
- "header": {
241
- "title": {"tag": "plain_text", "content": title},
242
- "template": template,
243
- },
244
- "elements": [
245
- {"tag": "div", "text": {"tag": "lark_md", "content": markdown_content}}
246
- ],
247
- }
248
- payload = {"msg_type": "interactive", "card": card}
249
-
250
- try:
251
- resp = requests.post(
252
- self.webhook_url, headers=headers, data=json.dumps(payload)
253
- )
254
- if resp.ok:
255
- result = resp.json()
256
- return bool(result.get("StatusCode") == 0 or result.get("code") == 0)
257
- else:
258
- print(f"发送文件消息失败: {resp.status_code}, {resp.text}")
259
- return False
260
- except Exception as e:
261
- print(f"发送文件消息异常: {e}")
262
- return False
263
-
264
- async def send_file_async(self, file_url: str, title: str, template: str):
265
- """
266
- description:
267
- 异步发送文件到飞书群
268
- parameters:
269
- file_url(str): 文件下载链接
270
- title(str): 消息标题
271
- template(str): 卡片模板颜色
272
- return:
273
- success(bool): 发送是否成功
274
- """
275
- return await asyncio.to_thread(self.send_file, file_url, title, template)
File without changes
File without changes
File without changes
File without changes