cloudoll 2.3.2__tar.gz → 2.4.0__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. {cloudoll-2.3.2 → cloudoll-2.4.0}/PKG-INFO +1 -2
  2. cloudoll-2.4.0/cloudoll/__init__.py +1 -0
  3. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/common.py +9 -3
  4. cloudoll-2.4.0/cloudoll/web/requests.py +161 -0
  5. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/PKG-INFO +1 -2
  6. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/SOURCES.txt +1 -5
  7. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/requires.txt +0 -1
  8. {cloudoll-2.3.2 → cloudoll-2.4.0}/setup.py +0 -1
  9. cloudoll-2.3.2/cloudoll/__init__.py +0 -1
  10. cloudoll-2.3.2/cloudoll/robot/dingtalk.py +0 -209
  11. cloudoll-2.3.2/cloudoll/robot/feishu.py +0 -102
  12. cloudoll-2.3.2/cloudoll/robot/slack.py +0 -115
  13. cloudoll-2.3.2/cloudoll/utils/__init__.py +0 -0
  14. cloudoll-2.3.2/cloudoll/web/http.py +0 -79
  15. {cloudoll-2.3.2 → cloudoll-2.4.0}/LICENSE +0 -0
  16. {cloudoll-2.3.2 → cloudoll-2.4.0}/README.md +0 -0
  17. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/__main__.py +0 -0
  18. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/cli.py +0 -0
  19. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/error/__init__.py +0 -0
  20. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/error/errors.py +0 -0
  21. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/logging/__init__.py +0 -0
  22. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/mail/__init__.py +0 -0
  23. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/mail/smtp.py +0 -0
  24. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/__init__.py +0 -0
  25. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/base.py +0 -0
  26. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/err.py +0 -0
  27. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/field.py +0 -0
  28. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/model.py +0 -0
  29. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/mysql.py +0 -0
  30. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/parse.py +0 -0
  31. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/orm/postgres.py +0 -0
  32. {cloudoll-2.3.2/cloudoll/robot → cloudoll-2.4.0/cloudoll/utils}/__init__.py +0 -0
  33. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/cli_main.py +0 -0
  34. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/m2d.py +0 -0
  35. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/patch_loop.py +0 -0
  36. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/snowflake.py +0 -0
  37. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/utils/watch.py +0 -0
  38. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/__init__.py +0 -0
  39. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/core.py +0 -0
  40. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/exception.py +0 -0
  41. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/html.py +0 -0
  42. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/jwt.py +0 -0
  43. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll/web/settings.py +0 -0
  44. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/dependency_links.txt +0 -0
  45. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/entry_points.txt +0 -0
  46. {cloudoll-2.3.2 → cloudoll-2.4.0}/cloudoll.egg-info/top_level.txt +0 -0
  47. {cloudoll-2.3.2 → cloudoll-2.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudoll
3
- Version: 2.3.2
3
+ Version: 2.4.0
4
4
  Summary: 辅助快速创建可分布的微服务。
5
5
  Home-page: https://gitee.com/chuchur/cloudoll-py
6
6
  Author: chuchur
@@ -16,7 +16,6 @@ Requires-Python: >=3.6.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: watchfiles
19
- Requires-Dist: curl_cffi
20
19
  Requires-Dist: colorlog
21
20
  Requires-Dist: aiomysql
22
21
  Requires-Dist: aiopg
@@ -0,0 +1 @@
1
+ __version__ = "2.4.0"
@@ -1,4 +1,9 @@
1
1
  import asyncio
2
+ from ..logging import warning
3
+
4
+ EADDRINUSE: int
5
+
6
+
2
7
  class Object(dict):
3
8
  # __setattr__ = dict.__setitem__
4
9
  # __getattr__ = dict.__getitem__
@@ -7,20 +12,21 @@ class Object(dict):
7
12
 
8
13
  def __getattr__(self, key):
9
14
  return self.get(key, None)
10
-
15
+
11
16
  def __delattr__(self, key):
12
17
  try:
13
18
  del self[key]
14
19
  except KeyError:
15
20
  raise AttributeError(f"'DotDict' object has no attribute '{key}'")
16
-
21
+
17
22
  def __getstate__(self):
18
- '''to fix pickle.dumps '''
23
+ """to fix pickle.dumps"""
19
24
  return dict(self)
20
25
 
21
26
  def __setstate__(self, state):
22
27
  self.update(state)
23
28
 
29
+
24
30
  def chainMap(*dicts):
25
31
  merged_dict = Object()
26
32
  for d in dicts:
@@ -0,0 +1,161 @@
1
+ import aiohttp
2
+ import asyncio
3
+ from typing import Optional, Dict, Any, Union
4
+
5
+
6
+ class AsyncRequest:
7
+ def __init__(
8
+ self,
9
+ timeout: int = 30,
10
+ max_retries: int = 3,
11
+ retry_delay: float = 1.0,
12
+ session: Optional[aiohttp.ClientSession] = None,
13
+ ):
14
+ """
15
+ 异步HTTP请求客户端
16
+
17
+ :param timeout: 请求超时时间(秒)
18
+ :param max_retries: 最大重试次数
19
+ :param retry_delay: 重试延迟(秒)
20
+ :param session: 可复用的aiohttp会话
21
+ """
22
+ self.timeout = aiohttp.ClientTimeout(total=timeout)
23
+ self.max_retries = max_retries
24
+ self.retry_delay = retry_delay
25
+ self._session = session
26
+
27
+ async def request(
28
+ self,
29
+ method: str,
30
+ url: str,
31
+ params: Optional[Dict[str, Any]] = None,
32
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
33
+ json_data: Optional[Any] = None,
34
+ headers: Optional[Dict[str, str]] = None,
35
+ cookies: Optional[Dict[str, str]] = None,
36
+ allow_redirects: bool = True,
37
+ raise_for_status: bool = True,
38
+ ) -> aiohttp.ClientResponse:
39
+ """
40
+ 执行异步HTTP请求
41
+
42
+ :param method: HTTP方法 (GET, POST, PUT, DELETE等)
43
+ :param url: 请求URL
44
+ :param params: URL查询参数
45
+ :param data: 请求体数据 (表单或原始数据)
46
+ :param json_data: JSON格式的请求体数据
47
+ :param headers: 请求头
48
+ :param cookies: cookies
49
+ :param allow_redirects: 是否允许重定向
50
+ :param raise_for_status: 是否在HTTP错误状态时抛出异常
51
+ :return: aiohttp.ClientResponse
52
+ """
53
+ session = self._session or aiohttp.ClientSession(timeout=self.timeout)
54
+ last_exception = None
55
+
56
+ for attempt in range(self.max_retries):
57
+ try:
58
+ async with session.request(
59
+ method=method,
60
+ url=url,
61
+ params=params,
62
+ data=data,
63
+ json=json_data,
64
+ headers=headers,
65
+ cookies=cookies,
66
+ allow_redirects=allow_redirects,
67
+ ) as response:
68
+ if raise_for_status:
69
+ response.raise_for_status()
70
+ return response
71
+
72
+ except (aiohttp.ClientError, asyncio.TimeoutError) as e:
73
+ last_exception = e
74
+ if attempt < self.max_retries - 1:
75
+ await asyncio.sleep(self.retry_delay * (attempt + 1))
76
+ continue
77
+
78
+ if self._session is None:
79
+ await session.close()
80
+
81
+ raise last_exception or RuntimeError("Request failed")
82
+
83
+ async def get(
84
+ self,
85
+ url: str,
86
+ params: Optional[Dict[str, Any]] = None,
87
+ headers: Optional[Dict[str, str]] = None,
88
+ **kwargs
89
+ ) -> aiohttp.ClientResponse:
90
+ """发送GET请求"""
91
+ return await self.request("GET", url, params=params, headers=headers, **kwargs)
92
+
93
+ async def post(
94
+ self,
95
+ url: str,
96
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
97
+ json_data: Optional[Any] = None,
98
+ headers: Optional[Dict[str, str]] = None,
99
+ **kwargs
100
+ ) -> aiohttp.ClientResponse:
101
+ """发送POST请求"""
102
+ return await self.request(
103
+ "POST", url, data=data, json_data=json_data, headers=headers, **kwargs
104
+ )
105
+
106
+ async def put(
107
+ self,
108
+ url: str,
109
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
110
+ json_data: Optional[Any] = None,
111
+ headers: Optional[Dict[str, str]] = None,
112
+ **kwargs
113
+ ) -> aiohttp.ClientResponse:
114
+ """发送PUT请求"""
115
+ return await self.request(
116
+ "PUT", url, data=data, json_data=json_data, headers=headers, **kwargs
117
+ )
118
+
119
+ async def delete(
120
+ self, url: str, headers: Optional[Dict[str, str]] = None, **kwargs
121
+ ) -> aiohttp.ClientResponse:
122
+ """发送DELETE请求"""
123
+ return await self.request("DELETE", url, headers=headers, **kwargs)
124
+
125
+ async def close(self) -> None:
126
+ """关闭客户端会话"""
127
+ if self._session is not None:
128
+ await self._session.close()
129
+ self._session = None
130
+
131
+ async def __aenter__(self):
132
+ return self
133
+
134
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
135
+ await self.close()
136
+
137
+
138
+ # 使用示例
139
+ """
140
+ async def test():
141
+ async with AsyncRequest() as client:
142
+ try:
143
+ # GET请求示例
144
+ response = await client.get(
145
+ "https://httpbin.org/get",
146
+ params={"key": "value"},
147
+ headers={"User-Agent": "AsyncRequest/1.0"},
148
+ )
149
+ print(await response.json())
150
+
151
+ # POST请求示例
152
+ response = await client.post(
153
+ "https://httpbin.org/post",
154
+ json_data={"key": "value"},
155
+ headers={"Content-Type": "application/json"},
156
+ )
157
+ print(await response.json())
158
+
159
+ except Exception as e:
160
+ print(f"Request failed: {e}")
161
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudoll
3
- Version: 2.3.2
3
+ Version: 2.4.0
4
4
  Summary: 辅助快速创建可分布的微服务。
5
5
  Home-page: https://gitee.com/chuchur/cloudoll-py
6
6
  Author: chuchur
@@ -16,7 +16,6 @@ Requires-Python: >=3.6.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: watchfiles
19
- Requires-Dist: curl_cffi
20
19
  Requires-Dist: colorlog
21
20
  Requires-Dist: aiomysql
22
21
  Requires-Dist: aiopg
@@ -23,10 +23,6 @@ cloudoll/orm/model.py
23
23
  cloudoll/orm/mysql.py
24
24
  cloudoll/orm/parse.py
25
25
  cloudoll/orm/postgres.py
26
- cloudoll/robot/__init__.py
27
- cloudoll/robot/dingtalk.py
28
- cloudoll/robot/feishu.py
29
- cloudoll/robot/slack.py
30
26
  cloudoll/utils/__init__.py
31
27
  cloudoll/utils/cli_main.py
32
28
  cloudoll/utils/common.py
@@ -38,6 +34,6 @@ cloudoll/web/__init__.py
38
34
  cloudoll/web/core.py
39
35
  cloudoll/web/exception.py
40
36
  cloudoll/web/html.py
41
- cloudoll/web/http.py
42
37
  cloudoll/web/jwt.py
38
+ cloudoll/web/requests.py
43
39
  cloudoll/web/settings.py
@@ -1,5 +1,4 @@
1
1
  watchfiles
2
- curl_cffi
3
2
  colorlog
4
3
  aiomysql
5
4
  aiopg
@@ -25,7 +25,6 @@ VERSION = version.__version__
25
25
  # What packages are required for this module to be executed?
26
26
  REQUIRED = [
27
27
  "watchfiles",
28
- "curl_cffi",
29
28
  "colorlog",
30
29
  "aiomysql",
31
30
  "aiopg",
@@ -1 +0,0 @@
1
- __version__ = "2.3.2"
@@ -1,209 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- # docs: https://open.dingtalk.com/document/group/message-types-and-data-format
5
- __author__ = "chuchur/chuchur.com"
6
-
7
- import base64
8
- import hashlib
9
- import hmac
10
- from ..logging import print_error
11
- from curl_cffi import requests as http
12
- import time
13
- from urllib import parse
14
-
15
-
16
- class Client(object):
17
- def __init__(self, **kw):
18
- self._webhook = kw.get("webhook")
19
- self._secret = kw.get("secret")
20
- self._access_token = kw.get("access_token")
21
-
22
- def _get_sign(self):
23
- secret = self._secret
24
- if not secret:
25
- print_error("缺少secret")
26
- return None, None
27
-
28
- # 拼接timestamp和secret
29
- timestamp = str(round(time.time() * 1000))
30
- string_to_sign = "{}\n{}".format(timestamp, secret)
31
- hmac_code = hmac.new(
32
- secret.encode("utf-8"),
33
- string_to_sign.encode("utf-8"),
34
- digestmod=hashlib.sha256,
35
- ).digest()
36
-
37
- # 对结果进行base64处理
38
- sign = base64.b64encode(hmac_code).decode("utf-8")
39
- sign = parse.quote(sign)
40
- return timestamp, sign
41
-
42
- def upload(self, type: str, filepath: str):
43
- """
44
- 上传媒体
45
- :params access_token
46
- :params type 文件类型 image|voice|video|file
47
- :params filepath 要上传的文件路径
48
- """
49
- access_token = self._access_token
50
- if not access_token:
51
- print_error("请配置access_token")
52
- return False
53
- url = "https://oapi.dingtalk.com/media/upload?access_token=%s" % access_token
54
- with open(filepath, "rb") as f:
55
- files = {"media": f}
56
- result = http.post(url=url, files=files, data={"type": type})
57
- res = result.json()
58
- if res["errcode"] == 0:
59
- return res
60
- else:
61
- print_error(res["errmsg"])
62
-
63
- def send(self, data: dict):
64
- """
65
- 发送除签名外的自定义消息结构体 https://open.dingtalk.com/document/orgapp-server/message-types-and-data-format
66
- :params data 自定义消息结构体
67
- """
68
- webhook = self._webhook
69
- if not webhook:
70
- print_error("缺少webhook")
71
- return False
72
- timestamp, sign = self._get_sign()
73
- if not timestamp or not sign:
74
- return False
75
- url = webhook + "&timestamp=%s&sign=%s" % (timestamp, sign)
76
- result = http.post(
77
- url=url,
78
- json={**data},
79
- # data=data
80
- # data=json.dumps(data)
81
- )
82
- if result:
83
- res = result.json()
84
- if res["errcode"] == 0:
85
- print_error("发送成功!")
86
- return True
87
- else:
88
- print_error("发送失败")
89
- print_error(res["errmsg"])
90
- return False
91
-
92
- def send_text(self, text: str):
93
- """
94
- 发送文本消息
95
- :params text 消息内容
96
- """
97
-
98
- return self.send(
99
- data={
100
- "msgtype": "text",
101
- "text": {"content": text},
102
- }
103
- )
104
-
105
- def _send_media(self, type, media_id, **kw):
106
- """
107
- 发送媒体
108
- :params type 媒体类型 image|voice|video|file
109
- :params media_id 媒体ID
110
- """
111
- data = {"msgtype": type}
112
- data[type] = {"media_id": media_id}
113
- if type == "voice":
114
- data["voice"]["duration"] = kw.get("duration")
115
- return self.send(data)
116
-
117
- def send_markdown(self, title, text):
118
- """
119
- 发送Markdown消息
120
- :params title 标题
121
- :params text markdown结构体
122
- """
123
- return self.send(
124
- data={"msgtype": "markdown", "markdown": {"title": title, "text": text}}
125
- )
126
-
127
- def send_image(self, media_id: str):
128
- """
129
- 发送图片消息
130
- :params media_id 媒体ID
131
- """
132
- return self._send_media("image", media_id)
133
-
134
- def send_voice(self, media_id: str, duration: str):
135
- """
136
- 发送语音消息
137
- :params media_id 媒体ID
138
- :params duration 正整数,小于60,表示音频时长。
139
- """
140
- return self._send_media("voice", media_id, duration=duration)
141
-
142
- def send_file(self, media_id: str):
143
- """
144
- 发送文件消息
145
- :params media_id 媒体ID
146
- """
147
- return self._send_media("file", media_id)
148
-
149
- def send_link(self, messageUrl, picUrl, title, text):
150
- """
151
- 发送链接消息
152
- :params messageUrl 链接地址
153
- :params picUrl 链接的小图
154
- :params title 链接标题 100字内
155
- :params text 链接副标题 500字内
156
- """
157
- return self.send(
158
- data={
159
- "msgtype": "link",
160
- "link": {
161
- "picUrl": picUrl,
162
- "messageUrl": messageUrl,
163
- "title": title,
164
- "text": text,
165
- },
166
- }
167
- )
168
-
169
- def send_card(self, **kw):
170
- """
171
- 发送卡片消息
172
- :params title 标题
173
- :params text 消息内容 必填
174
- :params single_title 查看详情(按钮文字) btns 二选一
175
- :params single_url 查看详情(链接) btns 二选一
176
- :params btn_orientation 按钮排列顺序。0|1
177
- :params btns 按钮 [{title:'',actionURL:''}]
178
- """
179
- title = kw.get("title", "")
180
- text = kw.get("text", "")
181
- single_title = kw.get("single_title", "")
182
- single_url = kw.get("single_url", "")
183
- btn_orientation = kw.get("btn_orientation", 0)
184
- btns = kw.get("btns", []) or []
185
- return self.send(
186
- data={
187
- "msgtype": "actionCard",
188
- "actionCard": {
189
- "title": title,
190
- "text": text,
191
- "singleTitle": single_title,
192
- "singleUrl": single_url,
193
- "btnOrientation": btn_orientation,
194
- "btns": btns,
195
- },
196
- }
197
- )
198
-
199
- def send_freecard(self, links: list):
200
- """
201
- 发送卡片消息
202
- :params links [{ title:'',messageURL:'',picURL:''}]
203
- """
204
- return self.send(
205
- data={
206
- "msgtype": "feedCard",
207
- "feedCard": {"links": links},
208
- }
209
- )
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- __author__ = "chuchur/chuchur.com"
5
-
6
- import hashlib, hmac, time, base64
7
- from curl_cffi import requests as http
8
- from ..logging import print_error, print_info
9
-
10
-
11
- class Client(object):
12
- def __init__(self, **kw):
13
- self._webhook = kw.get("webhook")
14
- self._secret = kw.get("secret")
15
-
16
- def _get_sign(self):
17
- secret = self._secret
18
- if not secret:
19
- print_error("缺少secret")
20
- return None, None
21
- # 拼接timestamp和secret
22
- timestamp = int(time.time())
23
- string_to_sign = "{}\n{}".format(timestamp, secret)
24
- hmac_code = hmac.new(
25
- string_to_sign.encode("utf-8"), digestmod=hashlib.sha256
26
- ).digest()
27
-
28
- # 对结果进行base64处理
29
- sign = base64.b64encode(hmac_code).decode("utf-8")
30
-
31
- return timestamp, sign
32
-
33
- def send(self, data: dict):
34
- """
35
- 发送除签名外的自定义消息结构体
36
- :params data 自定义消息结构体
37
- """
38
- webhook = self._webhook
39
- if not webhook:
40
- print_error("缺少webhook")
41
- return False
42
- timestamp, sign = self._get_sign()
43
- if not timestamp or not sign:
44
- return False
45
- result = http.post(
46
- url=webhook,
47
- json={"timestamp": timestamp, "sign": sign, **data},
48
- )
49
- if result:
50
- res = result.json()
51
- if res["StatusCode"] == 0:
52
- print_info("发送成功")
53
- return False
54
- else:
55
- print_error("发送失败!")
56
- return True
57
-
58
- def send_text(self, text: str):
59
- """
60
- 发送文本消息
61
- :params text 消息内容
62
- """
63
- return self.send(
64
- data={
65
- "msg_type": "text",
66
- "content": {"text": text},
67
- }
68
- )
69
-
70
- def send_card(self, **kw):
71
- """
72
- 发送卡片消息
73
- :params content 卡片内容,str
74
- :params elements 参见 :https://open.feishu.cn/document/ukTMukTMukTM/uMjNwUjLzYDM14yM2ATN
75
- :params template 标题背景颜色 blue|wathet|truquoise|green|yellow|orange|red|carmine|violet|purple|indigo|grey
76
- :params i18n 多语言 {'zh_cn':'...'}
77
- """
78
- content = kw.get("content", "")
79
- i18n = kw.get("i18n", {})
80
- elements = kw.get("elements", [])
81
- template = kw.get("template", "blue")
82
- return self.send(
83
- data={
84
- "msg_type": "interactive",
85
- "card": {
86
- "config": {
87
- "wide_screen_mode": True,
88
- "enable_forward": True,
89
- "update_multi": True,
90
- },
91
- "header": {
92
- "title": {
93
- "tag": "plain_text",
94
- "content": content,
95
- "i18n": i18n,
96
- },
97
- "template": template,
98
- },
99
- "elements": elements,
100
- },
101
- }
102
- )
@@ -1,115 +0,0 @@
1
- from cloudoll.web import http
2
-
3
- """
4
- web_hook: https://infloww.slack.com/apps/A0F7XDUAZ-incoming-webhook?tab=settings&next_id=0
5
- blocks_info: https://api.slack.com/messaging/composing/layouts
6
- """
7
-
8
-
9
- class Header:
10
- def __init__(self, text: str):
11
- self._text = text
12
-
13
- def get_json(self):
14
- return {"type": "header", "text": {"type": "plain_text", "text": self._text}}
15
-
16
-
17
- class Text:
18
- def __init__(self, text: str | int):
19
- self._text = text
20
-
21
- def get_json(self):
22
- return {"type": "section", "text": {"type": "mrkdwn", "text": self._text}}
23
-
24
-
25
- class Field:
26
- def __init__(self, title: str | int, text: str | int):
27
- self._title = title
28
- self._text = text
29
-
30
- def get_json(self):
31
- return {"type": "mrkdwn", "text": f"{self._title}\n{self._text}"}
32
-
33
-
34
- class Link:
35
- def __init__(self, title: str | int, url: str | int):
36
- self._title = title
37
- self._url = url
38
-
39
- def get_json(self):
40
- return {
41
- "type": "context",
42
- "elements": [{"type": "mrkdwn", "text": f"<{self._url}|_{self._title}_>"}],
43
- }
44
-
45
-
46
- class Section:
47
- def __init__(self):
48
- self._fileds: list[Field] = []
49
-
50
- def add(self, field: Field):
51
- self._fileds.append(field.get_json())
52
- return self
53
-
54
- def get_json(self):
55
- return {"type": "section", "fileds": self._fileds}
56
-
57
-
58
- class Elements:
59
- def __init__(self):
60
- self._elements: list[Field] = []
61
-
62
- def add(self, field: Field):
63
- self._elements.append(field.get_json())
64
- return self
65
-
66
- def get_json(self):
67
- return {"type": "context", "elements": self._elements}
68
-
69
-
70
- class Divider:
71
- def __init__(self):
72
- self._fileds: list[Field] = []
73
-
74
- def get_json(self):
75
- return {"type": "divider"}
76
-
77
-
78
- class Blocks:
79
- def __init__(self):
80
- self._blocks = []
81
-
82
- def add(self, block: Section | Header | Text | Divider | Link):
83
- self._blocks.append(block)
84
- return self
85
-
86
- def add_header(self, text):
87
- self._blocks.append(Header(text))
88
- return self
89
-
90
- def add_link(self, title, url: str):
91
- self._blocks.append(Link(title, url))
92
- return self
93
-
94
- def add_text(self, text):
95
- self._blocks.append(Text(text))
96
- return self
97
-
98
- def add_divider(self):
99
- self._blocks.append(Divider())
100
- return self
101
-
102
- def get_json(self):
103
- blocks = []
104
- for block in self._blocks:
105
- blocks.append(block.get_json())
106
- return blocks
107
-
108
-
109
- class Slack:
110
- def __init__(self, url: str):
111
- self._url = url
112
-
113
- def send_notify(self, blocks: Blocks):
114
- res = http.post(url=self._url, json={"blocks": blocks})
115
- return res
File without changes
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- __author__ = "chuchur/chuchur.com"
5
- # from aiohttp.web import middleware
6
- from curl_cffi import requests
7
-
8
-
9
- class Client(object):
10
- def __init__(self):
11
- self.session = requests.Session()
12
-
13
- def requests(self, method, url, **kw) -> requests.Response:
14
- impersonate = kw.get("impersonate", None)
15
- if not impersonate:
16
- kw["impersonate"] = "chrome133a"
17
- fun = getattr(self.session, method, None)
18
- result: requests.Response = fun(url, **kw)
19
- return result
20
-
21
-
22
- http = Client()
23
-
24
-
25
- def get(url, **kw):
26
- """
27
- get 请求
28
- :params params=dict() 传参
29
- """
30
- return http.requests("get", url, **kw)
31
-
32
-
33
- def post(url, **kw):
34
- """
35
- post 请求
36
- :params data=dict() or json=dict() 传参
37
- """
38
- return http.requests("post", url, **kw)
39
-
40
-
41
- def put(url, **kw):
42
- """
43
- post 请求
44
- data=dict()传参
45
- """
46
- return http.requests("put", url, **kw)
47
-
48
-
49
- def delete(url, **kw):
50
- return http.requests("delete", url, **kw)
51
-
52
-
53
- def head(url, **kw):
54
- return http.requests("head", url, **kw)
55
-
56
-
57
- def option(url, **kw):
58
- return http.requests("option", url, **kw)
59
-
60
-
61
- def download(url, savepath=None, **kw):
62
- """
63
- 下载文件
64
- :params url 文件的地址
65
- :params savepath 保存路径
66
- """
67
- response = http.requests("get", url, **kw)
68
- if response.status_code == 200:
69
- # 如果传入了保存路径,保存文件
70
- if savepath:
71
- with open(savepath, "wb") as file:
72
- file.write(response.content)
73
- print(f"File saved to {savepath}")
74
- else:
75
- # 如果没有传入保存路径,返回文件内容
76
- return response.content
77
- else:
78
- print(f"Failed to download the file. Status code: {response.status_code}")
79
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes