dootask-tools 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- client.py +443 -0
- dootask_tools-0.0.1.dist-info/METADATA +289 -0
- dootask_tools-0.0.1.dist-info/RECORD +8 -0
- dootask_tools-0.0.1.dist-info/WHEEL +5 -0
- dootask_tools-0.0.1.dist-info/licenses/LICENSE +21 -0
- dootask_tools-0.0.1.dist-info/top_level.txt +3 -0
- exceptions.py +27 -0
- models.py +487 -0
client.py
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DooTask Tools 客户端
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from typing import Optional, List, Dict, Any, Union, TypeVar, Type
|
|
8
|
+
from datetime import datetime, timedelta
|
|
9
|
+
from dataclasses import asdict, is_dataclass
|
|
10
|
+
from urllib.parse import urlencode, quote
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
from models import *
|
|
15
|
+
from exceptions import *
|
|
16
|
+
|
|
17
|
+
T = TypeVar('T')
|
|
18
|
+
|
|
19
|
+
class DooTaskClient:
|
|
20
|
+
"""DooTask 客户端"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, token: str, server: str = "http://nginx", timeout: int = 10):
|
|
23
|
+
"""
|
|
24
|
+
初始化客户端
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
token: 认证令牌
|
|
28
|
+
server: 服务器地址
|
|
29
|
+
timeout: 请求超时时间(秒)
|
|
30
|
+
"""
|
|
31
|
+
self.token = token
|
|
32
|
+
self.server = server.rstrip('/')
|
|
33
|
+
self.timeout = timeout
|
|
34
|
+
self._cache: Dict[str, Dict[str, Any]] = {}
|
|
35
|
+
self._cache_time = 600 # 10分钟缓存
|
|
36
|
+
|
|
37
|
+
# 创建会话
|
|
38
|
+
self.session = requests.Session()
|
|
39
|
+
self.session.headers.update({
|
|
40
|
+
'Token': token,
|
|
41
|
+
'User-Agent': 'DooTask-Tools/1.0',
|
|
42
|
+
'Content-Type': 'application/json'
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
def _build_url(self, base_url: str, params: Optional[Dict[str, Any]] = None) -> str:
|
|
46
|
+
"""构建带查询参数的URL"""
|
|
47
|
+
if not params:
|
|
48
|
+
return base_url
|
|
49
|
+
|
|
50
|
+
query_params = []
|
|
51
|
+
for key, value in params.items():
|
|
52
|
+
if value is None:
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
if isinstance(value, bool):
|
|
56
|
+
query_params.append(f"{key}={1 if value else 0}")
|
|
57
|
+
elif isinstance(value, str):
|
|
58
|
+
if value:
|
|
59
|
+
query_params.append(f"{key}={quote(value)}")
|
|
60
|
+
elif isinstance(value, (int, float)):
|
|
61
|
+
query_params.append(f"{key}={value}")
|
|
62
|
+
elif isinstance(value, list):
|
|
63
|
+
for item in value:
|
|
64
|
+
if isinstance(item, str):
|
|
65
|
+
query_params.append(f"{key}[]={quote(item)}")
|
|
66
|
+
else:
|
|
67
|
+
query_params.append(f"{key}[]={item}")
|
|
68
|
+
else:
|
|
69
|
+
query_params.append(f"{key}={quote(str(value))}")
|
|
70
|
+
|
|
71
|
+
if not query_params:
|
|
72
|
+
return base_url
|
|
73
|
+
|
|
74
|
+
separator = "&" if "?" in base_url else "?"
|
|
75
|
+
return base_url + separator + "&".join(query_params)
|
|
76
|
+
|
|
77
|
+
def _dataclass_to_dict(self, obj: Any) -> Dict[str, Any]:
|
|
78
|
+
"""将数据类转换为字典"""
|
|
79
|
+
if obj is None:
|
|
80
|
+
return {}
|
|
81
|
+
|
|
82
|
+
if is_dataclass(obj):
|
|
83
|
+
return asdict(obj)
|
|
84
|
+
|
|
85
|
+
if isinstance(obj, dict):
|
|
86
|
+
return obj
|
|
87
|
+
|
|
88
|
+
# 如果是普通对象,尝试转换为字典
|
|
89
|
+
if hasattr(obj, '__dict__'):
|
|
90
|
+
return obj.__dict__
|
|
91
|
+
|
|
92
|
+
return {}
|
|
93
|
+
|
|
94
|
+
def _make_request(self, method: str, api: str, request_data: Any = None,
|
|
95
|
+
response_type: Optional[Type[T]] = None,
|
|
96
|
+
headers: Optional[Dict[str, Any]] = None) -> Optional[T]:
|
|
97
|
+
"""发送请求"""
|
|
98
|
+
url = self.server + api
|
|
99
|
+
|
|
100
|
+
# 设置请求头
|
|
101
|
+
request_headers = self.session.headers.copy()
|
|
102
|
+
if headers:
|
|
103
|
+
request_headers.update(headers)
|
|
104
|
+
|
|
105
|
+
# 处理请求数据
|
|
106
|
+
if method.upper() == 'GET':
|
|
107
|
+
# GET 请求:将数据作为查询参数
|
|
108
|
+
if request_data:
|
|
109
|
+
params = self._dataclass_to_dict(request_data)
|
|
110
|
+
url = self._build_url(url, params)
|
|
111
|
+
|
|
112
|
+
response = self.session.get(url, timeout=self.timeout)
|
|
113
|
+
|
|
114
|
+
elif method.upper() in ['POST', 'PUT', 'PATCH']:
|
|
115
|
+
# POST/PUT/PATCH 请求:将数据作为 JSON body
|
|
116
|
+
json_data = None
|
|
117
|
+
if request_data:
|
|
118
|
+
json_data = self._dataclass_to_dict(request_data)
|
|
119
|
+
|
|
120
|
+
response = self.session.request(
|
|
121
|
+
method, url, json=json_data,
|
|
122
|
+
headers=request_headers, timeout=self.timeout
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
elif method.upper() == 'DELETE':
|
|
126
|
+
# DELETE 请求:支持查询参数
|
|
127
|
+
if request_data:
|
|
128
|
+
params = self._dataclass_to_dict(request_data)
|
|
129
|
+
url = self._build_url(url, params)
|
|
130
|
+
|
|
131
|
+
response = self.session.delete(url, timeout=self.timeout)
|
|
132
|
+
|
|
133
|
+
else:
|
|
134
|
+
raise DooTaskException(f"不支持的 HTTP 方法: {method}")
|
|
135
|
+
|
|
136
|
+
# 检查 HTTP 状态码
|
|
137
|
+
if response.status_code != 200:
|
|
138
|
+
raise DooTaskHTTPException(
|
|
139
|
+
f"HTTP {response.status_code}: {response.reason}, body: {response.text}",
|
|
140
|
+
response.status_code
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# 解析响应
|
|
144
|
+
try:
|
|
145
|
+
api_response = response.json()
|
|
146
|
+
except (json.JSONDecodeError, ValueError):
|
|
147
|
+
raise DooTaskException("响应不是有效的 JSON 格式")
|
|
148
|
+
|
|
149
|
+
# 检查业务状态
|
|
150
|
+
if api_response.get('ret') != 1:
|
|
151
|
+
error_msg = api_response.get('msg', f"API 错误: {api_response.get('ret', 'unknown')}")
|
|
152
|
+
raise DooTaskAPIException(error_msg, api_response.get('ret', 0))
|
|
153
|
+
|
|
154
|
+
# 如果不需要响应数据,直接返回 None
|
|
155
|
+
if response_type is None:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
# 解析数据到目标类型
|
|
159
|
+
data = api_response.get('data')
|
|
160
|
+
if data is None:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
# 如果是原始类型,直接返回
|
|
164
|
+
if response_type in [str, int, float, bool, dict, list]:
|
|
165
|
+
return data
|
|
166
|
+
|
|
167
|
+
# 如果是数据类,进行转换
|
|
168
|
+
if is_dataclass(response_type):
|
|
169
|
+
if isinstance(data, dict):
|
|
170
|
+
return response_type(**data)
|
|
171
|
+
elif isinstance(data, list):
|
|
172
|
+
return [response_type(**item) if isinstance(item, dict) else item for item in data]
|
|
173
|
+
|
|
174
|
+
return data
|
|
175
|
+
|
|
176
|
+
def _get_request(self, api: str, request_data: Any = None,
|
|
177
|
+
response_type: Optional[Type[T]] = None,
|
|
178
|
+
headers: Optional[Dict[str, Any]] = None) -> Optional[T]:
|
|
179
|
+
"""发送 GET 请求"""
|
|
180
|
+
return self._make_request('GET', api, request_data, response_type, headers)
|
|
181
|
+
|
|
182
|
+
def _post_request(self, api: str, request_data: Any = None,
|
|
183
|
+
response_type: Optional[Type[T]] = None) -> Optional[T]:
|
|
184
|
+
"""发送 POST 请求"""
|
|
185
|
+
return self._make_request('POST', api, request_data, response_type)
|
|
186
|
+
|
|
187
|
+
# ------------------------------------------------------------------------------------------
|
|
188
|
+
# 用户相关接口
|
|
189
|
+
# ------------------------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
def get_user_info(self, no_cache: bool = False) -> UserInfo:
|
|
192
|
+
"""获取用户信息"""
|
|
193
|
+
cache_key = f"user_info_{self.token}"
|
|
194
|
+
|
|
195
|
+
# 检查缓存
|
|
196
|
+
if not no_cache and cache_key in self._cache:
|
|
197
|
+
cache_data = self._cache[cache_key]
|
|
198
|
+
if time.time() < cache_data['expires_at']:
|
|
199
|
+
return cache_data['data']
|
|
200
|
+
|
|
201
|
+
# 获取用户信息
|
|
202
|
+
user_info = self._get_request('/api/users/info', response_type=UserInfo)
|
|
203
|
+
|
|
204
|
+
# 更新缓存
|
|
205
|
+
self._cache[cache_key] = {
|
|
206
|
+
'data': user_info,
|
|
207
|
+
'expires_at': time.time() + self._cache_time
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return user_info
|
|
211
|
+
|
|
212
|
+
def check_user_identity(self, identity: str) -> UserInfo:
|
|
213
|
+
"""检查用户是否具有指定身份"""
|
|
214
|
+
user = self.get_user_info()
|
|
215
|
+
|
|
216
|
+
if identity not in user.identity:
|
|
217
|
+
raise DooTaskPermissionException("权限不足")
|
|
218
|
+
|
|
219
|
+
return user
|
|
220
|
+
|
|
221
|
+
def get_user_departments(self) -> List[Department]:
|
|
222
|
+
"""获取用户部门信息"""
|
|
223
|
+
return self._get_request('/api/users/info/departments', response_type=List[Department])
|
|
224
|
+
|
|
225
|
+
def get_users_basic(self, userids: List[int]) -> List[UserBasic]:
|
|
226
|
+
"""获取指定用户基础信息(支持多个用户)"""
|
|
227
|
+
params = {'userid': userids}
|
|
228
|
+
return self._get_request('/api/users/basic', params, response_type=List[UserBasic])
|
|
229
|
+
|
|
230
|
+
def get_user_basic(self, userid: int) -> UserBasic:
|
|
231
|
+
"""获取指定用户基础信息(单个用户)"""
|
|
232
|
+
users = self.get_users_basic([userid])
|
|
233
|
+
if not users:
|
|
234
|
+
raise DooTaskException("用户不存在")
|
|
235
|
+
return users[0]
|
|
236
|
+
|
|
237
|
+
# ------------------------------------------------------------------------------------------
|
|
238
|
+
# 消息相关接口
|
|
239
|
+
# ------------------------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
def send_message(self, message: SendMessageRequest) -> None:
|
|
242
|
+
"""发送消息"""
|
|
243
|
+
if not message.text_type:
|
|
244
|
+
message.text_type = "md"
|
|
245
|
+
|
|
246
|
+
self._post_request('/api/dialog/msg/sendtext', message)
|
|
247
|
+
|
|
248
|
+
def send_message_to_user(self, message: SendMessageToUserRequest) -> None:
|
|
249
|
+
"""发送消息到用户"""
|
|
250
|
+
# 获取用户对话ID
|
|
251
|
+
query_params = {'userid': message.userid}
|
|
252
|
+
response = self._get_request('/api/dialog/open/user', query_params,
|
|
253
|
+
response_type=DialogOpenUserResponse)
|
|
254
|
+
|
|
255
|
+
# 发送消息
|
|
256
|
+
self.send_message(SendMessageRequest(
|
|
257
|
+
dialog_id=response.dialog_user.dialog_id,
|
|
258
|
+
text=message.text,
|
|
259
|
+
text_type=message.text_type,
|
|
260
|
+
silence=message.silence
|
|
261
|
+
))
|
|
262
|
+
|
|
263
|
+
def send_bot_message(self, message: SendBotMessageRequest) -> None:
|
|
264
|
+
"""发送机器人消息"""
|
|
265
|
+
if not message.bot_type:
|
|
266
|
+
message.bot_type = "system-msg"
|
|
267
|
+
|
|
268
|
+
self._post_request('/api/dialog/msg/sendbot', message)
|
|
269
|
+
|
|
270
|
+
def send_anonymous_message(self, message: SendAnonymousMessageRequest) -> None:
|
|
271
|
+
"""发送匿名消息"""
|
|
272
|
+
self._post_request('/api/dialog/msg/sendanon', message)
|
|
273
|
+
|
|
274
|
+
# ------------------------------------------------------------------------------------------
|
|
275
|
+
# 对话相关接口
|
|
276
|
+
# ------------------------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
def get_dialog_list(self, params: Optional[TimeRangeRequest] = None) -> ResponsePaginate[DialogInfo]:
|
|
279
|
+
"""获取对话列表"""
|
|
280
|
+
if params is None:
|
|
281
|
+
params = TimeRangeRequest()
|
|
282
|
+
return self._get_request('/api/dialog/lists', params, response_type=ResponsePaginate[DialogInfo])
|
|
283
|
+
|
|
284
|
+
def search_dialog(self, params: SearchDialogRequest) -> List[DialogInfo]:
|
|
285
|
+
"""搜索会话"""
|
|
286
|
+
return self._get_request('/api/dialog/search', params, response_type=List[DialogInfo])
|
|
287
|
+
|
|
288
|
+
def get_dialog_one(self, params: GetDialogRequest) -> DialogInfo:
|
|
289
|
+
"""获取单个会话信息"""
|
|
290
|
+
return self._get_request('/api/dialog/one', params, response_type=DialogInfo)
|
|
291
|
+
|
|
292
|
+
def get_dialog_user(self, params: GetDialogUserRequest) -> List[DialogMember]:
|
|
293
|
+
"""获取会话成员"""
|
|
294
|
+
return self._get_request('/api/dialog/user', params, response_type=List[DialogMember])
|
|
295
|
+
|
|
296
|
+
# ------------------------------------------------------------------------------------------
|
|
297
|
+
# 群组相关接口
|
|
298
|
+
# ------------------------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
def create_group(self, params: CreateGroupRequest) -> DialogInfo:
|
|
301
|
+
"""新增群组"""
|
|
302
|
+
return self._get_request('/api/dialog/group/add', params, response_type=DialogInfo)
|
|
303
|
+
|
|
304
|
+
def edit_group(self, params: EditGroupRequest) -> None:
|
|
305
|
+
"""修改群组"""
|
|
306
|
+
self._get_request('/api/dialog/group/edit', params)
|
|
307
|
+
|
|
308
|
+
def add_group_user(self, params: AddGroupUserRequest) -> None:
|
|
309
|
+
"""添加群成员"""
|
|
310
|
+
self._get_request('/api/dialog/group/adduser', params)
|
|
311
|
+
|
|
312
|
+
def remove_group_user(self, params: RemoveGroupUserRequest) -> None:
|
|
313
|
+
"""移除群成员"""
|
|
314
|
+
self._get_request('/api/dialog/group/deluser', params)
|
|
315
|
+
|
|
316
|
+
def exit_group(self, dialog_id: int) -> None:
|
|
317
|
+
"""退出群组"""
|
|
318
|
+
self.remove_group_user(RemoveGroupUserRequest(dialog_id=dialog_id))
|
|
319
|
+
|
|
320
|
+
def transfer_group(self, params: TransferGroupRequest) -> None:
|
|
321
|
+
"""转让群组"""
|
|
322
|
+
self._get_request('/api/dialog/group/transfer', params)
|
|
323
|
+
|
|
324
|
+
def disband_group(self, params: DisbandGroupRequest) -> None:
|
|
325
|
+
"""解散群组"""
|
|
326
|
+
self._get_request('/api/dialog/group/disband', params)
|
|
327
|
+
|
|
328
|
+
# ------------------------------------------------------------------------------------------
|
|
329
|
+
# 项目管理相关接口
|
|
330
|
+
# ------------------------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
def get_project_list(self, params: Optional[GetProjectListRequest] = None) -> ResponsePaginate[Project]:
|
|
333
|
+
"""获取项目列表"""
|
|
334
|
+
if params is None:
|
|
335
|
+
params = GetProjectListRequest()
|
|
336
|
+
return self._get_request('/api/project/lists', params, response_type=ResponsePaginate[Project])
|
|
337
|
+
|
|
338
|
+
def get_project(self, params: GetProjectRequest) -> Project:
|
|
339
|
+
"""获取项目信息"""
|
|
340
|
+
return self._get_request('/api/project/one', params, response_type=Project)
|
|
341
|
+
|
|
342
|
+
def create_project(self, params: CreateProjectRequest) -> Project:
|
|
343
|
+
"""创建项目"""
|
|
344
|
+
return self._get_request('/api/project/add', params, response_type=Project)
|
|
345
|
+
|
|
346
|
+
def update_project(self, params: UpdateProjectRequest) -> Project:
|
|
347
|
+
"""更新项目"""
|
|
348
|
+
return self._get_request('/api/project/update', params, response_type=Project)
|
|
349
|
+
|
|
350
|
+
def exit_project(self, project_id: int) -> None:
|
|
351
|
+
"""退出项目"""
|
|
352
|
+
params = ProjectActionRequest(project_id=project_id)
|
|
353
|
+
self._get_request('/api/project/exit', params)
|
|
354
|
+
|
|
355
|
+
def delete_project(self, project_id: int) -> None:
|
|
356
|
+
"""删除项目"""
|
|
357
|
+
params = ProjectActionRequest(project_id=project_id)
|
|
358
|
+
self._get_request('/api/project/remove', params)
|
|
359
|
+
|
|
360
|
+
# ------------------------------------------------------------------------------------------
|
|
361
|
+
# 任务列表相关接口
|
|
362
|
+
# ------------------------------------------------------------------------------------------
|
|
363
|
+
|
|
364
|
+
def get_column_list(self, params: GetColumnListRequest) -> ResponsePaginate[ProjectColumn]:
|
|
365
|
+
"""获取任务列表"""
|
|
366
|
+
return self._get_request('/api/project/column/lists', params,
|
|
367
|
+
response_type=ResponsePaginate[ProjectColumn])
|
|
368
|
+
|
|
369
|
+
def create_column(self, params: CreateColumnRequest) -> ProjectColumn:
|
|
370
|
+
"""创建任务列表"""
|
|
371
|
+
return self._get_request('/api/project/column/add', params, response_type=ProjectColumn)
|
|
372
|
+
|
|
373
|
+
def update_column(self, params: UpdateColumnRequest) -> ProjectColumn:
|
|
374
|
+
"""更新任务列表"""
|
|
375
|
+
return self._get_request('/api/project/column/update', params, response_type=ProjectColumn)
|
|
376
|
+
|
|
377
|
+
def delete_column(self, column_id: int) -> None:
|
|
378
|
+
"""删除任务列表"""
|
|
379
|
+
params = ColumnActionRequest(column_id=column_id)
|
|
380
|
+
self._get_request('/api/project/column/remove', params)
|
|
381
|
+
|
|
382
|
+
# ------------------------------------------------------------------------------------------
|
|
383
|
+
# 任务相关接口
|
|
384
|
+
# ------------------------------------------------------------------------------------------
|
|
385
|
+
|
|
386
|
+
def get_task_list(self, params: Optional[GetTaskListRequest] = None) -> ResponsePaginate[ProjectTask]:
|
|
387
|
+
"""获取任务列表"""
|
|
388
|
+
if params is None:
|
|
389
|
+
params = GetTaskListRequest()
|
|
390
|
+
return self._get_request('/api/project/task/lists', params,
|
|
391
|
+
response_type=ResponsePaginate[ProjectTask])
|
|
392
|
+
|
|
393
|
+
def get_task(self, params: GetTaskRequest) -> ProjectTask:
|
|
394
|
+
"""获取任务信息"""
|
|
395
|
+
return self._get_request('/api/project/task/one', params, response_type=ProjectTask)
|
|
396
|
+
|
|
397
|
+
def get_task_content(self, params: GetTaskContentRequest) -> TaskContent:
|
|
398
|
+
"""获取任务内容"""
|
|
399
|
+
return self._get_request('/api/project/task/content', params, response_type=TaskContent)
|
|
400
|
+
|
|
401
|
+
def get_task_files(self, params: GetTaskFilesRequest) -> List[TaskFile]:
|
|
402
|
+
"""获取任务文件列表"""
|
|
403
|
+
return self._get_request('/api/project/task/files', params, response_type=List[TaskFile])
|
|
404
|
+
|
|
405
|
+
def create_task(self, params: CreateTaskRequest) -> ProjectTask:
|
|
406
|
+
"""创建任务"""
|
|
407
|
+
return self._post_request('/api/project/task/add', params, response_type=ProjectTask)
|
|
408
|
+
|
|
409
|
+
def create_sub_task(self, params: CreateSubTaskRequest) -> ProjectTask:
|
|
410
|
+
"""创建子任务"""
|
|
411
|
+
return self._get_request('/api/project/task/addsub', params, response_type=ProjectTask)
|
|
412
|
+
|
|
413
|
+
def update_task(self, params: UpdateTaskRequest) -> ProjectTask:
|
|
414
|
+
"""更新任务"""
|
|
415
|
+
return self._post_request('/api/project/task/update', params, response_type=ProjectTask)
|
|
416
|
+
|
|
417
|
+
def create_task_dialog(self, params: CreateTaskDialogRequest) -> CreateTaskDialogResponse:
|
|
418
|
+
"""创建任务对话"""
|
|
419
|
+
return self._get_request('/api/project/task/dialog', params,
|
|
420
|
+
response_type=CreateTaskDialogResponse)
|
|
421
|
+
|
|
422
|
+
def archive_task(self, task_id: int, archive_type: str = "add") -> None:
|
|
423
|
+
"""归档任务"""
|
|
424
|
+
params = TaskActionRequest(task_id=task_id, type=archive_type)
|
|
425
|
+
self._get_request('/api/project/task/archived', params)
|
|
426
|
+
|
|
427
|
+
def delete_task(self, task_id: int, delete_type: str = "delete") -> None:
|
|
428
|
+
"""删除任务"""
|
|
429
|
+
params = TaskActionRequest(task_id=task_id, type=delete_type)
|
|
430
|
+
self._get_request('/api/project/task/remove', params)
|
|
431
|
+
|
|
432
|
+
# ------------------------------------------------------------------------------------------
|
|
433
|
+
# 系统设置相关方法
|
|
434
|
+
# ------------------------------------------------------------------------------------------
|
|
435
|
+
|
|
436
|
+
def get_system_settings(self) -> SystemSettings:
|
|
437
|
+
"""获取系统设置"""
|
|
438
|
+
return self._get_request('/api/system/setting', response_type=SystemSettings)
|
|
439
|
+
|
|
440
|
+
def get_version(self) -> VersionInfo:
|
|
441
|
+
"""获取版本信息"""
|
|
442
|
+
headers = {'version': 'true'}
|
|
443
|
+
return self._get_request('/api/system/version', headers=headers, response_type=VersionInfo)
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dootask-tools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: DooTask Tools 客户端库
|
|
5
|
+
Home-page: https://github.com/dootask/dootask-tools
|
|
6
|
+
Author: DooTask Team
|
|
7
|
+
Author-email: support@dootask.com
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/dootask/dootask-tools/issues
|
|
9
|
+
Project-URL: Source, https://github.com/dootask/dootask-tools
|
|
10
|
+
Project-URL: Documentation, https://github.com/dootask/dootask-tools#readme
|
|
11
|
+
Keywords: dootask api client chat project management collaboration
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Communications :: Chat
|
|
25
|
+
Classifier: Topic :: Office/Business :: Groupware
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
27
|
+
Requires-Python: >=3.7
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: requests>=2.28.0
|
|
31
|
+
Requires-Dist: dataclasses>=0.8; python_version < "3.7"
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: keywords
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: project-url
|
|
41
|
+
Dynamic: requires-dist
|
|
42
|
+
Dynamic: requires-python
|
|
43
|
+
Dynamic: summary
|
|
44
|
+
|
|
45
|
+
# DooTask Tools
|
|
46
|
+
|
|
47
|
+
一个用于与 DooTask 系统交互的 Python 客户端库,提供了完整的 API 封装和类型支持。
|
|
48
|
+
|
|
49
|
+
## 安装
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install dootask-tools
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 快速开始
|
|
56
|
+
|
|
57
|
+
### 初始化客户端
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from dootask import DooTaskClient
|
|
61
|
+
|
|
62
|
+
# 创建客户端
|
|
63
|
+
client = DooTaskClient(
|
|
64
|
+
token="your_token_here",
|
|
65
|
+
server="https://your-dootask-server.com"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# 获取用户信息
|
|
69
|
+
user = client.get_user_info()
|
|
70
|
+
print(f"用户: {user.nickname}")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 发送消息示例
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from dootask import SendMessageRequest, SendMessageToUserRequest
|
|
77
|
+
|
|
78
|
+
# 发送消息到指定对话
|
|
79
|
+
client.send_message(SendMessageRequest(
|
|
80
|
+
dialog_id=123,
|
|
81
|
+
text="Hello, World!",
|
|
82
|
+
text_type="md"
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
# 发送消息到用户
|
|
86
|
+
client.send_message_to_user(SendMessageToUserRequest(
|
|
87
|
+
userid=456,
|
|
88
|
+
text="私信内容",
|
|
89
|
+
text_type="md"
|
|
90
|
+
))
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 项目管理示例
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from dootask import GetProjectListRequest, CreateProjectRequest, CreateTaskRequest
|
|
97
|
+
|
|
98
|
+
# 获取项目列表
|
|
99
|
+
projects = client.get_project_list(GetProjectListRequest(
|
|
100
|
+
page=1,
|
|
101
|
+
pagesize=20
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
# 创建项目
|
|
105
|
+
project = client.create_project(CreateProjectRequest(
|
|
106
|
+
name="新项目",
|
|
107
|
+
desc="项目描述"
|
|
108
|
+
))
|
|
109
|
+
|
|
110
|
+
# 创建任务
|
|
111
|
+
task = client.create_task(CreateTaskRequest(
|
|
112
|
+
project_id=project.id,
|
|
113
|
+
name="新任务",
|
|
114
|
+
content="任务内容"
|
|
115
|
+
))
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## API 方法列表
|
|
119
|
+
|
|
120
|
+
### 客户端配置
|
|
121
|
+
|
|
122
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
123
|
+
|------|------|------|--------|
|
|
124
|
+
| `DooTaskClient` | 创建客户端实例 | `token, server, timeout` | `DooTaskClient` |
|
|
125
|
+
|
|
126
|
+
### 用户相关接口
|
|
127
|
+
|
|
128
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
129
|
+
|------|------|------|--------|
|
|
130
|
+
| `get_user_info` | 获取用户信息 | `no_cache=False` | `UserInfo` |
|
|
131
|
+
| `check_user_identity` | 检查用户身份 | `identity` | `UserInfo` |
|
|
132
|
+
| `get_user_departments` | 获取用户部门信息 | - | `List[Department]` |
|
|
133
|
+
| `get_users_basic` | 获取多个用户基础信息 | `userids: List[int]` | `List[UserBasic]` |
|
|
134
|
+
| `get_user_basic` | 获取单个用户基础信息 | `userid: int` | `UserBasic` |
|
|
135
|
+
|
|
136
|
+
### 消息相关接口
|
|
137
|
+
|
|
138
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
139
|
+
|------|------|------|--------|
|
|
140
|
+
| `send_message` | 发送消息 | `SendMessageRequest` | `None` |
|
|
141
|
+
| `send_message_to_user` | 发送消息到用户 | `SendMessageToUserRequest` | `None` |
|
|
142
|
+
| `send_bot_message` | 发送机器人消息 | `SendBotMessageRequest` | `None` |
|
|
143
|
+
| `send_anonymous_message` | 发送匿名消息 | `SendAnonymousMessageRequest` | `None` |
|
|
144
|
+
|
|
145
|
+
### 对话相关接口
|
|
146
|
+
|
|
147
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
148
|
+
|------|------|------|--------|
|
|
149
|
+
| `get_dialog_list` | 获取对话列表 | `TimeRangeRequest` | `ResponsePaginate[DialogInfo]` |
|
|
150
|
+
| `search_dialog` | 搜索会话 | `SearchDialogRequest` | `List[DialogInfo]` |
|
|
151
|
+
| `get_dialog_one` | 获取单个会话信息 | `GetDialogRequest` | `DialogInfo` |
|
|
152
|
+
| `get_dialog_user` | 获取会话成员 | `GetDialogUserRequest` | `List[DialogMember]` |
|
|
153
|
+
|
|
154
|
+
### 群组相关接口
|
|
155
|
+
|
|
156
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
157
|
+
|------|------|------|--------|
|
|
158
|
+
| `create_group` | 创建群组 | `CreateGroupRequest` | `DialogInfo` |
|
|
159
|
+
| `edit_group` | 修改群组 | `EditGroupRequest` | `None` |
|
|
160
|
+
| `add_group_user` | 添加群成员 | `AddGroupUserRequest` | `None` |
|
|
161
|
+
| `remove_group_user` | 移除群成员 | `RemoveGroupUserRequest` | `None` |
|
|
162
|
+
| `exit_group` | 退出群组 | `dialog_id: int` | `None` |
|
|
163
|
+
| `transfer_group` | 转让群组 | `TransferGroupRequest` | `None` |
|
|
164
|
+
| `disband_group` | 解散群组 | `DisbandGroupRequest` | `None` |
|
|
165
|
+
|
|
166
|
+
### 项目管理相关接口
|
|
167
|
+
|
|
168
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
169
|
+
|------|------|------|--------|
|
|
170
|
+
| `get_project_list` | 获取项目列表 | `GetProjectListRequest` | `ResponsePaginate[Project]` |
|
|
171
|
+
| `get_project` | 获取项目信息 | `GetProjectRequest` | `Project` |
|
|
172
|
+
| `create_project` | 创建项目 | `CreateProjectRequest` | `Project` |
|
|
173
|
+
| `update_project` | 更新项目 | `UpdateProjectRequest` | `Project` |
|
|
174
|
+
| `exit_project` | 退出项目 | `project_id: int` | `None` |
|
|
175
|
+
| `delete_project` | 删除项目 | `project_id: int` | `None` |
|
|
176
|
+
|
|
177
|
+
### 任务列表相关接口
|
|
178
|
+
|
|
179
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
180
|
+
|------|------|------|--------|
|
|
181
|
+
| `get_column_list` | 获取任务列表 | `GetColumnListRequest` | `ResponsePaginate[ProjectColumn]` |
|
|
182
|
+
| `create_column` | 创建任务列表 | `CreateColumnRequest` | `ProjectColumn` |
|
|
183
|
+
| `update_column` | 更新任务列表 | `UpdateColumnRequest` | `ProjectColumn` |
|
|
184
|
+
| `delete_column` | 删除任务列表 | `column_id: int` | `None` |
|
|
185
|
+
|
|
186
|
+
### 任务相关接口
|
|
187
|
+
|
|
188
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
189
|
+
|------|------|------|--------|
|
|
190
|
+
| `get_task_list` | 获取任务列表 | `GetTaskListRequest` | `ResponsePaginate[ProjectTask]` |
|
|
191
|
+
| `get_task` | 获取任务信息 | `GetTaskRequest` | `ProjectTask` |
|
|
192
|
+
| `get_task_content` | 获取任务内容 | `GetTaskContentRequest` | `TaskContent` |
|
|
193
|
+
| `get_task_files` | 获取任务文件列表 | `GetTaskFilesRequest` | `List[TaskFile]` |
|
|
194
|
+
| `create_task` | 创建任务 | `CreateTaskRequest` | `ProjectTask` |
|
|
195
|
+
| `create_sub_task` | 创建子任务 | `CreateSubTaskRequest` | `ProjectTask` |
|
|
196
|
+
| `update_task` | 更新任务 | `UpdateTaskRequest` | `ProjectTask` |
|
|
197
|
+
| `create_task_dialog` | 创建任务对话 | `CreateTaskDialogRequest` | `CreateTaskDialogResponse` |
|
|
198
|
+
| `archive_task` | 归档任务 | `task_id: int, archive_type: str` | `None` |
|
|
199
|
+
| `delete_task` | 删除任务 | `task_id: int, delete_type: str` | `None` |
|
|
200
|
+
|
|
201
|
+
### 系统设置相关接口
|
|
202
|
+
|
|
203
|
+
| 方法 | 描述 | 参数 | 返回值 |
|
|
204
|
+
|------|------|------|--------|
|
|
205
|
+
| `get_system_settings` | 获取系统设置 | - | `SystemSettings` |
|
|
206
|
+
| `get_version` | 获取版本信息 | - | `VersionInfo` |
|
|
207
|
+
|
|
208
|
+
## 主要数据类型
|
|
209
|
+
|
|
210
|
+
- `UserInfo` - 用户信息
|
|
211
|
+
- `UserBasic` - 用户基础信息
|
|
212
|
+
- `Department` - 部门信息
|
|
213
|
+
- `DialogInfo` - 对话信息
|
|
214
|
+
- `DialogMember` - 对话成员
|
|
215
|
+
- `Project` - 项目信息
|
|
216
|
+
- `ProjectColumn` - 项目列表
|
|
217
|
+
- `ProjectTask` - 项目任务
|
|
218
|
+
- `TaskFile` - 任务文件
|
|
219
|
+
- `TaskContent` - 任务内容
|
|
220
|
+
- `SystemSettings` - 系统设置
|
|
221
|
+
- `VersionInfo` - 版本信息
|
|
222
|
+
|
|
223
|
+
## 详细示例
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from dootask import (
|
|
227
|
+
DooTaskClient,
|
|
228
|
+
CreateProjectRequest,
|
|
229
|
+
CreateTaskRequest,
|
|
230
|
+
DooTaskException
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
client = DooTaskClient(
|
|
234
|
+
token="your_token",
|
|
235
|
+
server="https://your-server.com"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
# 创建项目
|
|
240
|
+
project = client.create_project(CreateProjectRequest(
|
|
241
|
+
name="我的项目",
|
|
242
|
+
desc="项目描述"
|
|
243
|
+
))
|
|
244
|
+
|
|
245
|
+
# 创建任务
|
|
246
|
+
task = client.create_task(CreateTaskRequest(
|
|
247
|
+
project_id=project.id,
|
|
248
|
+
name="任务名称",
|
|
249
|
+
content="任务内容",
|
|
250
|
+
owner=[user.userid]
|
|
251
|
+
))
|
|
252
|
+
|
|
253
|
+
print(f"项目创建成功: {project.name}")
|
|
254
|
+
print(f"任务创建成功: {task.name}")
|
|
255
|
+
|
|
256
|
+
except DooTaskException as e:
|
|
257
|
+
print(f"错误: {e}")
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 异常处理
|
|
261
|
+
|
|
262
|
+
所有方法都可能抛出异常,包含详细的错误信息:
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from dootask import (
|
|
266
|
+
DooTaskException,
|
|
267
|
+
DooTaskAPIException,
|
|
268
|
+
DooTaskHTTPException,
|
|
269
|
+
DooTaskAuthException,
|
|
270
|
+
DooTaskPermissionException
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
user = client.get_user_info()
|
|
275
|
+
except DooTaskAuthException:
|
|
276
|
+
print("认证失败,请检查 token")
|
|
277
|
+
except DooTaskPermissionException:
|
|
278
|
+
print("权限不足")
|
|
279
|
+
except DooTaskAPIException as e:
|
|
280
|
+
print(f"API 错误: {e}")
|
|
281
|
+
except DooTaskHTTPException as e:
|
|
282
|
+
print(f"网络错误: {e}")
|
|
283
|
+
except DooTaskException as e:
|
|
284
|
+
print(f"其他错误: {e}")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## 许可证
|
|
288
|
+
|
|
289
|
+
MIT License
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
client.py,sha256=5gN0cJDuy1MN42iL4OYfyAyWKVN6M_DUreFVgeliDYY,18393
|
|
2
|
+
exceptions.py,sha256=xsyQ_x13Tz-NNV9g3vHRbR4PHO_Fxgb-a7Ci522e4uA,703
|
|
3
|
+
models.py,sha256=V37I0Qt0RKugydiax7a0zmmGGF1nMpdgwlvkdvHK84M,11057
|
|
4
|
+
dootask_tools-0.0.1.dist-info/licenses/LICENSE,sha256=XZTR4JqVYnbWD7E7Z8ID68qgYx_reZd9dpU5MameQOM,1069
|
|
5
|
+
dootask_tools-0.0.1.dist-info/METADATA,sha256=j6Cl6W4R1iFXOgStHdy-tbFsWxSiorxBdEn2vgQkgzo,9137
|
|
6
|
+
dootask_tools-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
dootask_tools-0.0.1.dist-info/top_level.txt,sha256=6Tc1kkb0pPCjirIzy28Nxe70crS6gyxjtP1pvmjaQYc,25
|
|
8
|
+
dootask_tools-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 DooTask Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
exceptions.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DooTask Tools 异常定义
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class DooTaskException(Exception):
|
|
6
|
+
"""DooTask 基础异常类"""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class DooTaskAPIException(DooTaskException):
|
|
10
|
+
"""DooTask API 异常"""
|
|
11
|
+
def __init__(self, message: str, ret_code: int = 0):
|
|
12
|
+
super().__init__(message)
|
|
13
|
+
self.ret_code = ret_code
|
|
14
|
+
|
|
15
|
+
class DooTaskHTTPException(DooTaskException):
|
|
16
|
+
"""DooTask HTTP 异常"""
|
|
17
|
+
def __init__(self, message: str, status_code: int = 0):
|
|
18
|
+
super().__init__(message)
|
|
19
|
+
self.status_code = status_code
|
|
20
|
+
|
|
21
|
+
class DooTaskAuthException(DooTaskException):
|
|
22
|
+
"""DooTask 认证异常"""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class DooTaskPermissionException(DooTaskException):
|
|
26
|
+
"""DooTask 权限异常"""
|
|
27
|
+
pass
|
models.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DooTask Tools 数据类型定义
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Optional, List, Dict, Any, Union, Generic, TypeVar
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
T = TypeVar('T')
|
|
10
|
+
|
|
11
|
+
# ------------------------------------------------------------------------------------------
|
|
12
|
+
# 基础响应结构
|
|
13
|
+
# ------------------------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class Response(Generic[T]):
|
|
17
|
+
"""基础响应结构"""
|
|
18
|
+
ret: int
|
|
19
|
+
msg: str
|
|
20
|
+
data: Optional[T] = None
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ResponsePaginate(Generic[T]):
|
|
24
|
+
"""分页数据"""
|
|
25
|
+
current_page: int
|
|
26
|
+
data: List[T]
|
|
27
|
+
next_page_url: Optional[str] = None
|
|
28
|
+
path: str = ""
|
|
29
|
+
per_page: Union[int, str] = 0
|
|
30
|
+
prev_page_url: Optional[str] = None
|
|
31
|
+
to: Union[int, str] = 0
|
|
32
|
+
total: int = 0
|
|
33
|
+
|
|
34
|
+
# ------------------------------------------------------------------------------------------
|
|
35
|
+
# 用户相关结构
|
|
36
|
+
# ------------------------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class UserInfo:
|
|
40
|
+
"""用户信息"""
|
|
41
|
+
userid: int
|
|
42
|
+
identity: List[str] = field(default_factory=list)
|
|
43
|
+
email: str = ""
|
|
44
|
+
nickname: str = ""
|
|
45
|
+
profession: str = ""
|
|
46
|
+
userimg: str = ""
|
|
47
|
+
bot: int = 0
|
|
48
|
+
department: List[int] = field(default_factory=list)
|
|
49
|
+
department_name: str = ""
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class UserBasic:
|
|
53
|
+
"""用户基础信息"""
|
|
54
|
+
userid: int
|
|
55
|
+
email: str = ""
|
|
56
|
+
nickname: str = ""
|
|
57
|
+
profession: str = ""
|
|
58
|
+
userimg: str = ""
|
|
59
|
+
bot: int = 0
|
|
60
|
+
online: bool = False
|
|
61
|
+
department: List[int] = field(default_factory=list)
|
|
62
|
+
department_name: str = ""
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class Department:
|
|
66
|
+
"""部门信息"""
|
|
67
|
+
id: int
|
|
68
|
+
name: str
|
|
69
|
+
parent_id: int = 0
|
|
70
|
+
owner_userid: int = 0
|
|
71
|
+
|
|
72
|
+
# ------------------------------------------------------------------------------------------
|
|
73
|
+
# 对话相关结构
|
|
74
|
+
# ------------------------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class DialogUserResponse:
|
|
78
|
+
"""对话用户信息"""
|
|
79
|
+
dialog_id: int
|
|
80
|
+
userid: int
|
|
81
|
+
bot: int = 0
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class DialogOpenUserResponse:
|
|
85
|
+
"""打开用户对话响应数据"""
|
|
86
|
+
dialog_user: DialogUserResponse
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class DialogInfo:
|
|
90
|
+
"""对话信息"""
|
|
91
|
+
id: int
|
|
92
|
+
type: str = ""
|
|
93
|
+
group_type: str = ""
|
|
94
|
+
name: str = ""
|
|
95
|
+
avatar: str = ""
|
|
96
|
+
owner_id: int = 0
|
|
97
|
+
created_at: str = ""
|
|
98
|
+
updated_at: str = ""
|
|
99
|
+
last_at: str = ""
|
|
100
|
+
mark_unread: int = 0
|
|
101
|
+
silence: int = 0
|
|
102
|
+
hide: int = 0
|
|
103
|
+
color: str = ""
|
|
104
|
+
unread: int = 0
|
|
105
|
+
unread_one: int = 0
|
|
106
|
+
mention: int = 0
|
|
107
|
+
mention_ids: List[int] = field(default_factory=list)
|
|
108
|
+
people: int = 0
|
|
109
|
+
people_user: int = 0
|
|
110
|
+
people_bot: int = 0
|
|
111
|
+
todo_num: int = 0
|
|
112
|
+
last_msg: Any = None
|
|
113
|
+
pinyin: str = ""
|
|
114
|
+
bot: int = 0
|
|
115
|
+
top_at: str = ""
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class DialogMember:
|
|
119
|
+
"""会话成员信息"""
|
|
120
|
+
id: int
|
|
121
|
+
dialog_id: int
|
|
122
|
+
userid: int
|
|
123
|
+
nickname: str = ""
|
|
124
|
+
email: str = ""
|
|
125
|
+
userimg: str = ""
|
|
126
|
+
bot: int = 0
|
|
127
|
+
online: bool = False
|
|
128
|
+
|
|
129
|
+
# ------------------------------------------------------------------------------------------
|
|
130
|
+
# 消息相关结构
|
|
131
|
+
# ------------------------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class SendMessageRequest:
|
|
135
|
+
"""发送消息请求"""
|
|
136
|
+
dialog_id: int
|
|
137
|
+
text: str
|
|
138
|
+
text_type: str = "md"
|
|
139
|
+
silence: bool = False
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class SendMessageToUserRequest:
|
|
143
|
+
"""发送消息到用户请求"""
|
|
144
|
+
userid: int
|
|
145
|
+
text: str
|
|
146
|
+
text_type: str = "md"
|
|
147
|
+
silence: bool = False
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
class SendBotMessageRequest:
|
|
151
|
+
"""发送机器人消息请求"""
|
|
152
|
+
userid: int
|
|
153
|
+
text: str
|
|
154
|
+
bot_type: str = "system-msg"
|
|
155
|
+
bot_name: str = ""
|
|
156
|
+
silence: bool = False
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class SendAnonymousMessageRequest:
|
|
160
|
+
"""发送匿名消息请求"""
|
|
161
|
+
userid: int
|
|
162
|
+
text: str
|
|
163
|
+
|
|
164
|
+
# ------------------------------------------------------------------------------------------
|
|
165
|
+
# 群组相关结构
|
|
166
|
+
# ------------------------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
@dataclass
|
|
169
|
+
class CreateGroupRequest:
|
|
170
|
+
"""创建群组请求"""
|
|
171
|
+
userids: List[int]
|
|
172
|
+
avatar: str = ""
|
|
173
|
+
chat_name: str = ""
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class EditGroupRequest:
|
|
177
|
+
"""修改群组请求"""
|
|
178
|
+
dialog_id: int
|
|
179
|
+
avatar: str = ""
|
|
180
|
+
chat_name: str = ""
|
|
181
|
+
admin: int = 0
|
|
182
|
+
|
|
183
|
+
@dataclass
|
|
184
|
+
class AddGroupUserRequest:
|
|
185
|
+
"""添加群成员请求"""
|
|
186
|
+
dialog_id: int
|
|
187
|
+
userids: List[int]
|
|
188
|
+
|
|
189
|
+
@dataclass
|
|
190
|
+
class RemoveGroupUserRequest:
|
|
191
|
+
"""移除群成员请求"""
|
|
192
|
+
dialog_id: int
|
|
193
|
+
userids: List[int] = field(default_factory=list)
|
|
194
|
+
|
|
195
|
+
@dataclass
|
|
196
|
+
class TransferGroupRequest:
|
|
197
|
+
"""转让群组请求"""
|
|
198
|
+
dialog_id: int
|
|
199
|
+
userid: int
|
|
200
|
+
check_owner: str = ""
|
|
201
|
+
key: str = ""
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
class DisbandGroupRequest:
|
|
205
|
+
"""解散群组请求"""
|
|
206
|
+
dialog_id: int
|
|
207
|
+
|
|
208
|
+
# ------------------------------------------------------------------------------------------
|
|
209
|
+
# 通用请求参数
|
|
210
|
+
# ------------------------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
@dataclass
|
|
213
|
+
class TimeRangeRequest:
|
|
214
|
+
"""时间范围请求参数"""
|
|
215
|
+
timerange: str = ""
|
|
216
|
+
page: int = 1
|
|
217
|
+
pagesize: int = 50
|
|
218
|
+
|
|
219
|
+
@dataclass
|
|
220
|
+
class SearchDialogRequest:
|
|
221
|
+
"""搜索会话请求"""
|
|
222
|
+
key: str
|
|
223
|
+
|
|
224
|
+
@dataclass
|
|
225
|
+
class GetDialogRequest:
|
|
226
|
+
"""获取单个会话请求"""
|
|
227
|
+
dialog_id: int
|
|
228
|
+
|
|
229
|
+
@dataclass
|
|
230
|
+
class GetDialogUserRequest:
|
|
231
|
+
"""获取会话成员请求"""
|
|
232
|
+
dialog_id: int
|
|
233
|
+
getuser: int = 0
|
|
234
|
+
|
|
235
|
+
# ------------------------------------------------------------------------------------------
|
|
236
|
+
# 项目管理相关结构
|
|
237
|
+
# ------------------------------------------------------------------------------------------
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class Project:
|
|
241
|
+
"""项目信息"""
|
|
242
|
+
id: int
|
|
243
|
+
name: str = ""
|
|
244
|
+
desc: str = ""
|
|
245
|
+
userid: int = 0
|
|
246
|
+
dialog_id: int = 0
|
|
247
|
+
archived_at: str = ""
|
|
248
|
+
created_at: str = ""
|
|
249
|
+
updated_at: str = ""
|
|
250
|
+
owner: int = 0
|
|
251
|
+
owner_userid: int = 0
|
|
252
|
+
personal: int = 0
|
|
253
|
+
# 任务统计
|
|
254
|
+
task_num: int = 0
|
|
255
|
+
task_complete: int = 0
|
|
256
|
+
task_percent: int = 0
|
|
257
|
+
task_my_num: int = 0
|
|
258
|
+
task_my_complete: int = 0
|
|
259
|
+
task_my_percent: int = 0
|
|
260
|
+
|
|
261
|
+
@dataclass
|
|
262
|
+
class ProjectColumn:
|
|
263
|
+
"""项目列表"""
|
|
264
|
+
id: int
|
|
265
|
+
project_id: int
|
|
266
|
+
name: str = ""
|
|
267
|
+
color: str = ""
|
|
268
|
+
sort: int = 0
|
|
269
|
+
created_at: str = ""
|
|
270
|
+
updated_at: str = ""
|
|
271
|
+
|
|
272
|
+
@dataclass
|
|
273
|
+
class ProjectTask:
|
|
274
|
+
"""项目任务"""
|
|
275
|
+
id: int
|
|
276
|
+
project_id: int = 0
|
|
277
|
+
column_id: int = 0
|
|
278
|
+
parent_id: int = 0
|
|
279
|
+
name: str = ""
|
|
280
|
+
desc: str = ""
|
|
281
|
+
start_at: str = ""
|
|
282
|
+
end_at: str = ""
|
|
283
|
+
complete_at: str = ""
|
|
284
|
+
archived_at: str = ""
|
|
285
|
+
created_at: str = ""
|
|
286
|
+
updated_at: str = ""
|
|
287
|
+
userid: int = 0
|
|
288
|
+
dialog_id: int = 0
|
|
289
|
+
flow_item_id: int = 0
|
|
290
|
+
flow_item_name: str = ""
|
|
291
|
+
visibility: int = 0
|
|
292
|
+
color: str = ""
|
|
293
|
+
# 统计信息
|
|
294
|
+
file_num: int = 0
|
|
295
|
+
msg_num: int = 0
|
|
296
|
+
sub_num: int = 0
|
|
297
|
+
sub_complete: int = 0
|
|
298
|
+
percent: int = 0
|
|
299
|
+
# 关联数据
|
|
300
|
+
project_name: str = ""
|
|
301
|
+
column_name: str = ""
|
|
302
|
+
|
|
303
|
+
@dataclass
|
|
304
|
+
class TaskFile:
|
|
305
|
+
"""任务文件"""
|
|
306
|
+
id: int
|
|
307
|
+
task_id: int
|
|
308
|
+
name: str = ""
|
|
309
|
+
ext: str = ""
|
|
310
|
+
size: int = 0
|
|
311
|
+
path: str = ""
|
|
312
|
+
thumb: str = ""
|
|
313
|
+
userid: int = 0
|
|
314
|
+
created_at: str = ""
|
|
315
|
+
updated_at: str = ""
|
|
316
|
+
|
|
317
|
+
@dataclass
|
|
318
|
+
class TaskContent:
|
|
319
|
+
"""任务内容"""
|
|
320
|
+
content: str = ""
|
|
321
|
+
type: str = ""
|
|
322
|
+
|
|
323
|
+
# ------------------------------------------------------------------------------------------
|
|
324
|
+
# 项目管理请求参数
|
|
325
|
+
# ------------------------------------------------------------------------------------------
|
|
326
|
+
|
|
327
|
+
@dataclass
|
|
328
|
+
class GetProjectListRequest:
|
|
329
|
+
"""获取项目列表请求"""
|
|
330
|
+
type: str = "all"
|
|
331
|
+
archived: str = "no"
|
|
332
|
+
getcolumn: str = "no"
|
|
333
|
+
getuserid: str = "no"
|
|
334
|
+
getstatistics: str = "no"
|
|
335
|
+
timerange: str = ""
|
|
336
|
+
page: int = 1
|
|
337
|
+
pagesize: int = 50
|
|
338
|
+
|
|
339
|
+
@dataclass
|
|
340
|
+
class GetProjectRequest:
|
|
341
|
+
"""获取项目信息请求"""
|
|
342
|
+
project_id: int
|
|
343
|
+
|
|
344
|
+
@dataclass
|
|
345
|
+
class CreateProjectRequest:
|
|
346
|
+
"""创建项目请求"""
|
|
347
|
+
name: str
|
|
348
|
+
desc: str = ""
|
|
349
|
+
columns: str = ""
|
|
350
|
+
flow: str = ""
|
|
351
|
+
personal: int = 0
|
|
352
|
+
|
|
353
|
+
@dataclass
|
|
354
|
+
class UpdateProjectRequest:
|
|
355
|
+
"""更新项目请求"""
|
|
356
|
+
project_id: int
|
|
357
|
+
name: str
|
|
358
|
+
desc: str = ""
|
|
359
|
+
archive_method: str = ""
|
|
360
|
+
archive_days: int = 0
|
|
361
|
+
|
|
362
|
+
@dataclass
|
|
363
|
+
class ProjectActionRequest:
|
|
364
|
+
"""项目操作请求"""
|
|
365
|
+
project_id: int
|
|
366
|
+
type: str = ""
|
|
367
|
+
|
|
368
|
+
@dataclass
|
|
369
|
+
class GetColumnListRequest:
|
|
370
|
+
"""获取列表请求"""
|
|
371
|
+
project_id: int
|
|
372
|
+
page: int = 1
|
|
373
|
+
pagesize: int = 100
|
|
374
|
+
|
|
375
|
+
@dataclass
|
|
376
|
+
class CreateColumnRequest:
|
|
377
|
+
"""创建列表请求"""
|
|
378
|
+
project_id: int
|
|
379
|
+
name: str
|
|
380
|
+
|
|
381
|
+
@dataclass
|
|
382
|
+
class UpdateColumnRequest:
|
|
383
|
+
"""更新列表请求"""
|
|
384
|
+
column_id: int
|
|
385
|
+
name: str = ""
|
|
386
|
+
color: str = ""
|
|
387
|
+
|
|
388
|
+
@dataclass
|
|
389
|
+
class ColumnActionRequest:
|
|
390
|
+
"""列表操作请求"""
|
|
391
|
+
column_id: int
|
|
392
|
+
|
|
393
|
+
@dataclass
|
|
394
|
+
class GetTaskListRequest:
|
|
395
|
+
"""获取任务列表请求"""
|
|
396
|
+
project_id: int = 0
|
|
397
|
+
parent_id: int = 0
|
|
398
|
+
archived: str = "no"
|
|
399
|
+
deleted: str = "no"
|
|
400
|
+
timerange: str = ""
|
|
401
|
+
page: int = 1
|
|
402
|
+
pagesize: int = 100
|
|
403
|
+
|
|
404
|
+
@dataclass
|
|
405
|
+
class GetTaskRequest:
|
|
406
|
+
"""获取任务信息请求"""
|
|
407
|
+
task_id: int
|
|
408
|
+
archived: str = "no"
|
|
409
|
+
|
|
410
|
+
@dataclass
|
|
411
|
+
class GetTaskContentRequest:
|
|
412
|
+
"""获取任务内容请求"""
|
|
413
|
+
task_id: int
|
|
414
|
+
history_id: int = 0
|
|
415
|
+
|
|
416
|
+
@dataclass
|
|
417
|
+
class GetTaskFilesRequest:
|
|
418
|
+
"""获取任务文件请求"""
|
|
419
|
+
task_id: int
|
|
420
|
+
|
|
421
|
+
@dataclass
|
|
422
|
+
class CreateTaskRequest:
|
|
423
|
+
"""创建任务请求"""
|
|
424
|
+
project_id: int
|
|
425
|
+
name: str
|
|
426
|
+
column_id: Union[int, str] = 0
|
|
427
|
+
content: str = ""
|
|
428
|
+
times: List[str] = field(default_factory=list)
|
|
429
|
+
owner: List[int] = field(default_factory=list)
|
|
430
|
+
top: int = 0
|
|
431
|
+
|
|
432
|
+
@dataclass
|
|
433
|
+
class CreateSubTaskRequest:
|
|
434
|
+
"""创建子任务请求"""
|
|
435
|
+
task_id: int
|
|
436
|
+
name: str
|
|
437
|
+
|
|
438
|
+
@dataclass
|
|
439
|
+
class UpdateTaskRequest:
|
|
440
|
+
"""更新任务请求"""
|
|
441
|
+
task_id: int
|
|
442
|
+
name: str = ""
|
|
443
|
+
content: str = ""
|
|
444
|
+
times: List[str] = field(default_factory=list)
|
|
445
|
+
owner: List[int] = field(default_factory=list)
|
|
446
|
+
assist: List[int] = field(default_factory=list)
|
|
447
|
+
color: str = ""
|
|
448
|
+
visibility: int = 0
|
|
449
|
+
complete_at: Any = None
|
|
450
|
+
|
|
451
|
+
@dataclass
|
|
452
|
+
class TaskActionRequest:
|
|
453
|
+
"""任务操作请求"""
|
|
454
|
+
task_id: int
|
|
455
|
+
type: str = ""
|
|
456
|
+
|
|
457
|
+
@dataclass
|
|
458
|
+
class CreateTaskDialogRequest:
|
|
459
|
+
"""创建任务对话请求"""
|
|
460
|
+
task_id: int
|
|
461
|
+
|
|
462
|
+
@dataclass
|
|
463
|
+
class CreateTaskDialogResponse:
|
|
464
|
+
"""创建任务对话响应"""
|
|
465
|
+
id: int
|
|
466
|
+
dialog_id: int
|
|
467
|
+
dialog_data: Any = None
|
|
468
|
+
|
|
469
|
+
# ------------------------------------------------------------------------------------------
|
|
470
|
+
# 系统设置相关
|
|
471
|
+
# ------------------------------------------------------------------------------------------
|
|
472
|
+
|
|
473
|
+
@dataclass
|
|
474
|
+
class SystemSettings:
|
|
475
|
+
"""系统设置"""
|
|
476
|
+
reg: Optional[str] = None
|
|
477
|
+
task_default_time: Optional[List[str]] = None
|
|
478
|
+
system_alias: Optional[str] = None
|
|
479
|
+
system_welcome: str = ""
|
|
480
|
+
server_timezone: Optional[str] = None
|
|
481
|
+
server_version: Optional[str] = None
|
|
482
|
+
|
|
483
|
+
@dataclass
|
|
484
|
+
class VersionInfo:
|
|
485
|
+
"""版本信息"""
|
|
486
|
+
device_count: int = 0
|
|
487
|
+
version: str = ""
|