feishu_api_utils.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. import json
  2. from typing import Optional
  3. import httpx
  4. from core.tools.errors import ToolProviderCredentialValidationError
  5. from extensions.ext_redis import redis_client
  6. def auth(credentials):
  7. app_id = credentials.get("app_id")
  8. app_secret = credentials.get("app_secret")
  9. if not app_id or not app_secret:
  10. raise ToolProviderCredentialValidationError("app_id and app_secret is required")
  11. try:
  12. assert FeishuRequest(app_id, app_secret).tenant_access_token is not None
  13. except Exception as e:
  14. raise ToolProviderCredentialValidationError(str(e))
  15. def convert_add_records(json_str):
  16. try:
  17. data = json.loads(json_str)
  18. if not isinstance(data, list):
  19. raise ValueError("Parsed data must be a list")
  20. converted_data = [{"fields": json.dumps(item, ensure_ascii=False)} for item in data]
  21. return converted_data
  22. except json.JSONDecodeError:
  23. raise ValueError("The input string is not valid JSON")
  24. except Exception as e:
  25. raise ValueError(f"An error occurred while processing the data: {e}")
  26. def convert_update_records(json_str):
  27. try:
  28. data = json.loads(json_str)
  29. if not isinstance(data, list):
  30. raise ValueError("Parsed data must be a list")
  31. converted_data = [
  32. {"fields": json.dumps(record["fields"], ensure_ascii=False), "record_id": record["record_id"]}
  33. for record in data
  34. if "fields" in record and "record_id" in record
  35. ]
  36. if len(converted_data) != len(data):
  37. raise ValueError("Each record must contain 'fields' and 'record_id'")
  38. return converted_data
  39. except json.JSONDecodeError:
  40. raise ValueError("The input string is not valid JSON")
  41. except Exception as e:
  42. raise ValueError(f"An error occurred while processing the data: {e}")
  43. class FeishuRequest:
  44. API_BASE_URL = "https://lark-plugin-api.solutionsuite.cn/lark-plugin"
  45. def __init__(self, app_id: str, app_secret: str):
  46. self.app_id = app_id
  47. self.app_secret = app_secret
  48. @property
  49. def tenant_access_token(self):
  50. feishu_tenant_access_token = f"tools:{self.app_id}:feishu_tenant_access_token"
  51. if redis_client.exists(feishu_tenant_access_token):
  52. return redis_client.get(feishu_tenant_access_token).decode()
  53. res = self.get_tenant_access_token(self.app_id, self.app_secret)
  54. redis_client.setex(feishu_tenant_access_token, res.get("expire"), res.get("tenant_access_token"))
  55. return res.get("tenant_access_token")
  56. def _send_request(
  57. self,
  58. url: str,
  59. method: str = "post",
  60. require_token: bool = True,
  61. payload: Optional[dict] = None,
  62. params: Optional[dict] = None,
  63. ):
  64. headers = {
  65. "Content-Type": "application/json",
  66. "user-agent": "Dify",
  67. }
  68. if require_token:
  69. headers["tenant-access-token"] = f"{self.tenant_access_token}"
  70. res = httpx.request(method=method, url=url, headers=headers, json=payload, params=params, timeout=30).json()
  71. if res.get("code") != 0:
  72. raise Exception(res)
  73. return res
  74. def get_tenant_access_token(self, app_id: str, app_secret: str) -> dict:
  75. """
  76. API url: https://open.feishu.cn/document/server-docs/authentication-management/access-token/tenant_access_token_internal
  77. Example Response:
  78. {
  79. "code": 0,
  80. "msg": "ok",
  81. "tenant_access_token": "t-caecc734c2e3328a62489fe0648c4b98779515d3",
  82. "expire": 7200
  83. }
  84. """
  85. url = f"{self.API_BASE_URL}/access_token/get_tenant_access_token"
  86. payload = {"app_id": app_id, "app_secret": app_secret}
  87. res = self._send_request(url, require_token=False, payload=payload)
  88. return res
  89. def create_document(self, title: str, content: str, folder_token: str) -> dict:
  90. """
  91. API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/create
  92. Example Response:
  93. {
  94. "data": {
  95. "title": "title",
  96. "url": "https://svi136aogf123.feishu.cn/docx/VWbvd4fEdoW0WSxaY1McQTz8n7d",
  97. "type": "docx",
  98. "token": "VWbvd4fEdoW0WSxaY1McQTz8n7d"
  99. },
  100. "log_id": "021721281231575fdbddc0200ff00060a9258ec0000103df61b5d",
  101. "code": 0,
  102. "msg": "创建飞书文档成功,请查看"
  103. }
  104. """
  105. url = f"{self.API_BASE_URL}/document/create_document"
  106. payload = {
  107. "title": title,
  108. "content": content,
  109. "folder_token": folder_token,
  110. }
  111. res = self._send_request(url, payload=payload)
  112. return res.get("data")
  113. def write_document(self, document_id: str, content: str, position: str = "end") -> dict:
  114. url = f"{self.API_BASE_URL}/document/write_document"
  115. payload = {"document_id": document_id, "content": content, "position": position}
  116. res = self._send_request(url, payload=payload)
  117. return res
  118. def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> dict:
  119. """
  120. API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/raw_content
  121. Example Response:
  122. {
  123. "code": 0,
  124. "msg": "success",
  125. "data": {
  126. "content": "云文档\n多人实时协同,插入一切元素。不仅是在线文档,更是强大的创作和互动工具\n云文档:专为协作而生\n"
  127. }
  128. }
  129. """ # noqa: E501
  130. params = {
  131. "document_id": document_id,
  132. "mode": mode,
  133. "lang": lang,
  134. }
  135. url = f"{self.API_BASE_URL}/document/get_document_content"
  136. res = self._send_request(url, method="GET", params=params)
  137. return res.get("data").get("content")
  138. def list_document_blocks(
  139. self, document_id: str, page_token: str, user_id_type: str = "open_id", page_size: int = 500
  140. ) -> dict:
  141. """
  142. API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/list
  143. """
  144. params = {
  145. "user_id_type": user_id_type,
  146. "document_id": document_id,
  147. "page_size": page_size,
  148. "page_token": page_token,
  149. }
  150. url = f"{self.API_BASE_URL}/document/list_document_blocks"
  151. res = self._send_request(url, method="GET", params=params)
  152. return res.get("data")
  153. def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict:
  154. """
  155. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/create
  156. """
  157. url = f"{self.API_BASE_URL}/message/send_bot_message"
  158. params = {
  159. "receive_id_type": receive_id_type,
  160. }
  161. payload = {
  162. "receive_id": receive_id,
  163. "msg_type": msg_type,
  164. "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
  165. }
  166. res = self._send_request(url, params=params, payload=payload)
  167. return res.get("data")
  168. def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict:
  169. url = f"{self.API_BASE_URL}/message/send_webhook_message"
  170. payload = {
  171. "webhook": webhook,
  172. "msg_type": msg_type,
  173. "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
  174. }
  175. res = self._send_request(url, require_token=False, payload=payload)
  176. return res
  177. def get_chat_messages(
  178. self,
  179. container_id: str,
  180. start_time: str,
  181. end_time: str,
  182. page_token: str,
  183. sort_type: str = "ByCreateTimeAsc",
  184. page_size: int = 20,
  185. ) -> dict:
  186. """
  187. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
  188. """
  189. url = f"{self.API_BASE_URL}/message/get_chat_messages"
  190. params = {
  191. "container_id": container_id,
  192. "start_time": start_time,
  193. "end_time": end_time,
  194. "sort_type": sort_type,
  195. "page_token": page_token,
  196. "page_size": page_size,
  197. }
  198. res = self._send_request(url, method="GET", params=params)
  199. return res.get("data")
  200. def get_thread_messages(
  201. self, container_id: str, page_token: str, sort_type: str = "ByCreateTimeAsc", page_size: int = 20
  202. ) -> dict:
  203. """
  204. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
  205. """
  206. url = f"{self.API_BASE_URL}/message/get_thread_messages"
  207. params = {
  208. "container_id": container_id,
  209. "sort_type": sort_type,
  210. "page_token": page_token,
  211. "page_size": page_size,
  212. }
  213. res = self._send_request(url, method="GET", params=params)
  214. return res.get("data")
  215. def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict:
  216. # 创建任务
  217. url = f"{self.API_BASE_URL}/task/create_task"
  218. payload = {
  219. "summary": summary,
  220. "start_time": start_time,
  221. "end_time": end_time,
  222. "completed_at": completed_time,
  223. "description": description,
  224. }
  225. res = self._send_request(url, payload=payload)
  226. return res.get("data")
  227. def update_task(
  228. self, task_guid: str, summary: str, start_time: str, end_time: str, completed_time: str, description: str
  229. ) -> dict:
  230. # 更新任务
  231. url = f"{self.API_BASE_URL}/task/update_task"
  232. payload = {
  233. "task_guid": task_guid,
  234. "summary": summary,
  235. "start_time": start_time,
  236. "end_time": end_time,
  237. "completed_time": completed_time,
  238. "description": description,
  239. }
  240. res = self._send_request(url, method="PATCH", payload=payload)
  241. return res.get("data")
  242. def delete_task(self, task_guid: str) -> dict:
  243. # 删除任务
  244. url = f"{self.API_BASE_URL}/task/delete_task"
  245. payload = {
  246. "task_guid": task_guid,
  247. }
  248. res = self._send_request(url, method="DELETE", payload=payload)
  249. return res
  250. def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict:
  251. # 删除任务
  252. url = f"{self.API_BASE_URL}/task/add_members"
  253. payload = {
  254. "task_guid": task_guid,
  255. "member_phone_or_email": member_phone_or_email,
  256. "member_role": member_role,
  257. }
  258. res = self._send_request(url, payload=payload)
  259. return res
  260. def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict:
  261. # 获取知识库全部子节点列表
  262. url = f"{self.API_BASE_URL}/wiki/get_wiki_nodes"
  263. payload = {
  264. "space_id": space_id,
  265. "parent_node_token": parent_node_token,
  266. "page_token": page_token,
  267. "page_size": page_size,
  268. }
  269. res = self._send_request(url, payload=payload)
  270. return res.get("data")
  271. def get_primary_calendar(self, user_id_type: str = "open_id") -> dict:
  272. url = f"{self.API_BASE_URL}/calendar/get_primary_calendar"
  273. params = {
  274. "user_id_type": user_id_type,
  275. }
  276. res = self._send_request(url, method="GET", params=params)
  277. return res.get("data")
  278. def create_event(
  279. self,
  280. summary: str,
  281. description: str,
  282. start_time: str,
  283. end_time: str,
  284. attendee_ability: str,
  285. need_notification: bool = True,
  286. auto_record: bool = False,
  287. ) -> dict:
  288. url = f"{self.API_BASE_URL}/calendar/create_event"
  289. payload = {
  290. "summary": summary,
  291. "description": description,
  292. "need_notification": need_notification,
  293. "start_time": start_time,
  294. "end_time": end_time,
  295. "auto_record": auto_record,
  296. "attendee_ability": attendee_ability,
  297. }
  298. res = self._send_request(url, payload=payload)
  299. return res.get("data")
  300. def update_event(
  301. self,
  302. event_id: str,
  303. summary: str,
  304. description: str,
  305. need_notification: bool,
  306. start_time: str,
  307. end_time: str,
  308. auto_record: bool,
  309. ) -> dict:
  310. url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}"
  311. payload = {}
  312. if summary:
  313. payload["summary"] = summary
  314. if description:
  315. payload["description"] = description
  316. if start_time:
  317. payload["start_time"] = start_time
  318. if end_time:
  319. payload["end_time"] = end_time
  320. if need_notification:
  321. payload["need_notification"] = need_notification
  322. if auto_record:
  323. payload["auto_record"] = auto_record
  324. res = self._send_request(url, method="PATCH", payload=payload)
  325. return res
  326. def delete_event(self, event_id: str, need_notification: bool = True) -> dict:
  327. url = f"{self.API_BASE_URL}/calendar/delete_event/{event_id}"
  328. params = {
  329. "need_notification": need_notification,
  330. }
  331. res = self._send_request(url, method="DELETE", params=params)
  332. return res
  333. def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict:
  334. url = f"{self.API_BASE_URL}/calendar/list_events"
  335. params = {
  336. "start_time": start_time,
  337. "end_time": end_time,
  338. "page_token": page_token,
  339. "page_size": page_size,
  340. }
  341. res = self._send_request(url, method="GET", params=params)
  342. return res.get("data")
  343. def search_events(
  344. self,
  345. query: str,
  346. start_time: str,
  347. end_time: str,
  348. page_token: str,
  349. user_id_type: str = "open_id",
  350. page_size: int = 20,
  351. ) -> dict:
  352. url = f"{self.API_BASE_URL}/calendar/search_events"
  353. payload = {
  354. "query": query,
  355. "start_time": start_time,
  356. "end_time": end_time,
  357. "page_token": page_token,
  358. "user_id_type": user_id_type,
  359. "page_size": page_size,
  360. }
  361. res = self._send_request(url, payload=payload)
  362. return res.get("data")
  363. def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict:
  364. # 参加日程参会人
  365. url = f"{self.API_BASE_URL}/calendar/add_event_attendees"
  366. payload = {
  367. "event_id": event_id,
  368. "attendee_phone_or_email": attendee_phone_or_email,
  369. "need_notification": need_notification,
  370. }
  371. res = self._send_request(url, payload=payload)
  372. return res.get("data")
  373. def create_spreadsheet(
  374. self,
  375. title: str,
  376. folder_token: str,
  377. ) -> dict:
  378. # 创建电子表格
  379. url = f"{self.API_BASE_URL}/spreadsheet/create_spreadsheet"
  380. payload = {
  381. "title": title,
  382. "folder_token": folder_token,
  383. }
  384. res = self._send_request(url, payload=payload)
  385. return res.get("data")
  386. def get_spreadsheet(
  387. self,
  388. spreadsheet_token: str,
  389. user_id_type: str = "open_id",
  390. ) -> dict:
  391. # 获取电子表格信息
  392. url = f"{self.API_BASE_URL}/spreadsheet/get_spreadsheet"
  393. params = {
  394. "spreadsheet_token": spreadsheet_token,
  395. "user_id_type": user_id_type,
  396. }
  397. res = self._send_request(url, method="GET", params=params)
  398. return res.get("data")
  399. def list_spreadsheet_sheets(
  400. self,
  401. spreadsheet_token: str,
  402. ) -> dict:
  403. # 列出电子表格的所有工作表
  404. url = f"{self.API_BASE_URL}/spreadsheet/list_spreadsheet_sheets"
  405. params = {
  406. "spreadsheet_token": spreadsheet_token,
  407. }
  408. res = self._send_request(url, method="GET", params=params)
  409. return res.get("data")
  410. def add_rows(
  411. self,
  412. spreadsheet_token: str,
  413. sheet_id: str,
  414. sheet_name: str,
  415. length: int,
  416. values: str,
  417. ) -> dict:
  418. # 增加行,在工作表最后添加
  419. url = f"{self.API_BASE_URL}/spreadsheet/add_rows"
  420. payload = {
  421. "spreadsheet_token": spreadsheet_token,
  422. "sheet_id": sheet_id,
  423. "sheet_name": sheet_name,
  424. "length": length,
  425. "values": values,
  426. }
  427. res = self._send_request(url, payload=payload)
  428. return res.get("data")
  429. def add_cols(
  430. self,
  431. spreadsheet_token: str,
  432. sheet_id: str,
  433. sheet_name: str,
  434. length: int,
  435. values: str,
  436. ) -> dict:
  437. # 增加列,在工作表最后添加
  438. url = f"{self.API_BASE_URL}/spreadsheet/add_cols"
  439. payload = {
  440. "spreadsheet_token": spreadsheet_token,
  441. "sheet_id": sheet_id,
  442. "sheet_name": sheet_name,
  443. "length": length,
  444. "values": values,
  445. }
  446. res = self._send_request(url, payload=payload)
  447. return res.get("data")
  448. def read_rows(
  449. self,
  450. spreadsheet_token: str,
  451. sheet_id: str,
  452. sheet_name: str,
  453. start_row: int,
  454. num_rows: int,
  455. user_id_type: str = "open_id",
  456. ) -> dict:
  457. # 读取工作表行数据
  458. url = f"{self.API_BASE_URL}/spreadsheet/read_rows"
  459. params = {
  460. "spreadsheet_token": spreadsheet_token,
  461. "sheet_id": sheet_id,
  462. "sheet_name": sheet_name,
  463. "start_row": start_row,
  464. "num_rows": num_rows,
  465. "user_id_type": user_id_type,
  466. }
  467. res = self._send_request(url, method="GET", params=params)
  468. return res.get("data")
  469. def read_cols(
  470. self,
  471. spreadsheet_token: str,
  472. sheet_id: str,
  473. sheet_name: str,
  474. start_col: int,
  475. num_cols: int,
  476. user_id_type: str = "open_id",
  477. ) -> dict:
  478. # 读取工作表列数据
  479. url = f"{self.API_BASE_URL}/spreadsheet/read_cols"
  480. params = {
  481. "spreadsheet_token": spreadsheet_token,
  482. "sheet_id": sheet_id,
  483. "sheet_name": sheet_name,
  484. "start_col": start_col,
  485. "num_cols": num_cols,
  486. "user_id_type": user_id_type,
  487. }
  488. res = self._send_request(url, method="GET", params=params)
  489. return res.get("data")
  490. def read_table(
  491. self,
  492. spreadsheet_token: str,
  493. sheet_id: str,
  494. sheet_name: str,
  495. num_range: str,
  496. query: str,
  497. user_id_type: str = "open_id",
  498. ) -> dict:
  499. # 自定义读取行列数据
  500. url = f"{self.API_BASE_URL}/spreadsheet/read_table"
  501. params = {
  502. "spreadsheet_token": spreadsheet_token,
  503. "sheet_id": sheet_id,
  504. "sheet_name": sheet_name,
  505. "range": num_range,
  506. "query": query,
  507. "user_id_type": user_id_type,
  508. }
  509. res = self._send_request(url, method="GET", params=params)
  510. return res.get("data")
  511. def create_base(
  512. self,
  513. name: str,
  514. folder_token: str,
  515. ) -> dict:
  516. # 创建多维表格
  517. url = f"{self.API_BASE_URL}/base/create_base"
  518. payload = {
  519. "name": name,
  520. "folder_token": folder_token,
  521. }
  522. res = self._send_request(url, payload=payload)
  523. return res.get("data")
  524. def add_records(
  525. self,
  526. app_token: str,
  527. table_id: str,
  528. table_name: str,
  529. records: str,
  530. user_id_type: str = "open_id",
  531. ) -> dict:
  532. # 新增多条记录
  533. url = f"{self.API_BASE_URL}/base/add_records"
  534. params = {
  535. "app_token": app_token,
  536. "table_id": table_id,
  537. "table_name": table_name,
  538. "user_id_type": user_id_type,
  539. }
  540. payload = {
  541. "records": convert_add_records(records),
  542. }
  543. res = self._send_request(url, params=params, payload=payload)
  544. return res.get("data")
  545. def update_records(
  546. self,
  547. app_token: str,
  548. table_id: str,
  549. table_name: str,
  550. records: str,
  551. user_id_type: str,
  552. ) -> dict:
  553. # 更新多条记录
  554. url = f"{self.API_BASE_URL}/base/update_records"
  555. params = {
  556. "app_token": app_token,
  557. "table_id": table_id,
  558. "table_name": table_name,
  559. "user_id_type": user_id_type,
  560. }
  561. payload = {
  562. "records": convert_update_records(records),
  563. }
  564. res = self._send_request(url, params=params, payload=payload)
  565. return res.get("data")
  566. def delete_records(
  567. self,
  568. app_token: str,
  569. table_id: str,
  570. table_name: str,
  571. record_ids: str,
  572. ) -> dict:
  573. # 删除多条记录
  574. url = f"{self.API_BASE_URL}/base/delete_records"
  575. params = {
  576. "app_token": app_token,
  577. "table_id": table_id,
  578. "table_name": table_name,
  579. }
  580. if not record_ids:
  581. record_id_list = []
  582. else:
  583. try:
  584. record_id_list = json.loads(record_ids)
  585. except json.JSONDecodeError:
  586. raise ValueError("The input string is not valid JSON")
  587. payload = {
  588. "records": record_id_list,
  589. }
  590. res = self._send_request(url, params=params, payload=payload)
  591. return res.get("data")
  592. def search_record(
  593. self,
  594. app_token: str,
  595. table_id: str,
  596. table_name: str,
  597. view_id: str,
  598. field_names: str,
  599. sort: str,
  600. filters: str,
  601. page_token: str,
  602. automatic_fields: bool = False,
  603. user_id_type: str = "open_id",
  604. page_size: int = 20,
  605. ) -> dict:
  606. # 查询记录,单次最多查询 500 行记录。
  607. url = f"{self.API_BASE_URL}/base/search_record"
  608. params = {
  609. "app_token": app_token,
  610. "table_id": table_id,
  611. "table_name": table_name,
  612. "user_id_type": user_id_type,
  613. "page_token": page_token,
  614. "page_size": page_size,
  615. }
  616. if not field_names:
  617. field_name_list = []
  618. else:
  619. try:
  620. field_name_list = json.loads(field_names)
  621. except json.JSONDecodeError:
  622. raise ValueError("The input string is not valid JSON")
  623. if not sort:
  624. sort_list = []
  625. else:
  626. try:
  627. sort_list = json.loads(sort)
  628. except json.JSONDecodeError:
  629. raise ValueError("The input string is not valid JSON")
  630. if not filters:
  631. filter_dict = {}
  632. else:
  633. try:
  634. filter_dict = json.loads(filters)
  635. except json.JSONDecodeError:
  636. raise ValueError("The input string is not valid JSON")
  637. payload = {}
  638. if view_id:
  639. payload["view_id"] = view_id
  640. if field_names:
  641. payload["field_names"] = field_name_list
  642. if sort:
  643. payload["sort"] = sort_list
  644. if filters:
  645. payload["filter"] = filter_dict
  646. if automatic_fields:
  647. payload["automatic_fields"] = automatic_fields
  648. res = self._send_request(url, params=params, payload=payload)
  649. return res.get("data")
  650. def get_base_info(
  651. self,
  652. app_token: str,
  653. ) -> dict:
  654. # 获取多维表格元数据
  655. url = f"{self.API_BASE_URL}/base/get_base_info"
  656. params = {
  657. "app_token": app_token,
  658. }
  659. res = self._send_request(url, method="GET", params=params)
  660. return res.get("data")
  661. def create_table(
  662. self,
  663. app_token: str,
  664. table_name: str,
  665. default_view_name: str,
  666. fields: str,
  667. ) -> dict:
  668. # 新增一个数据表
  669. url = f"{self.API_BASE_URL}/base/create_table"
  670. params = {
  671. "app_token": app_token,
  672. }
  673. if not fields:
  674. fields_list = []
  675. else:
  676. try:
  677. fields_list = json.loads(fields)
  678. except json.JSONDecodeError:
  679. raise ValueError("The input string is not valid JSON")
  680. payload = {
  681. "name": table_name,
  682. "fields": fields_list,
  683. }
  684. if default_view_name:
  685. payload["default_view_name"] = default_view_name
  686. res = self._send_request(url, params=params, payload=payload)
  687. return res.get("data")
  688. def delete_tables(
  689. self,
  690. app_token: str,
  691. table_ids: str,
  692. table_names: str,
  693. ) -> dict:
  694. # 删除多个数据表
  695. url = f"{self.API_BASE_URL}/base/delete_tables"
  696. params = {
  697. "app_token": app_token,
  698. }
  699. if not table_ids:
  700. table_id_list = []
  701. else:
  702. try:
  703. table_id_list = json.loads(table_ids)
  704. except json.JSONDecodeError:
  705. raise ValueError("The input string is not valid JSON")
  706. if not table_names:
  707. table_name_list = []
  708. else:
  709. try:
  710. table_name_list = json.loads(table_names)
  711. except json.JSONDecodeError:
  712. raise ValueError("The input string is not valid JSON")
  713. payload = {
  714. "table_ids": table_id_list,
  715. "table_names": table_name_list,
  716. }
  717. res = self._send_request(url, params=params, payload=payload)
  718. return res.get("data")
  719. def list_tables(
  720. self,
  721. app_token: str,
  722. page_token: str,
  723. page_size: int = 20,
  724. ) -> dict:
  725. # 列出多维表格下的全部数据表
  726. url = f"{self.API_BASE_URL}/base/list_tables"
  727. params = {
  728. "app_token": app_token,
  729. "page_token": page_token,
  730. "page_size": page_size,
  731. }
  732. res = self._send_request(url, method="GET", params=params)
  733. return res.get("data")
  734. def read_records(
  735. self,
  736. app_token: str,
  737. table_id: str,
  738. table_name: str,
  739. record_ids: str,
  740. user_id_type: str = "open_id",
  741. ) -> dict:
  742. url = f"{self.API_BASE_URL}/base/read_records"
  743. params = {
  744. "app_token": app_token,
  745. "table_id": table_id,
  746. "table_name": table_name,
  747. }
  748. if not record_ids:
  749. record_id_list = []
  750. else:
  751. try:
  752. record_id_list = json.loads(record_ids)
  753. except json.JSONDecodeError:
  754. raise ValueError("The input string is not valid JSON")
  755. payload = {
  756. "record_ids": record_id_list,
  757. "user_id_type": user_id_type,
  758. }
  759. res = self._send_request(url, method="GET", params=params, payload=payload)
  760. return res.get("data")