feishu_api_utils.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  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. if "data" in res:
  113. return res.get("data")
  114. return res
  115. def write_document(self, document_id: str, content: str, position: str = "end") -> dict:
  116. url = f"{self.API_BASE_URL}/document/write_document"
  117. payload = {"document_id": document_id, "content": content, "position": position}
  118. res = self._send_request(url, payload=payload)
  119. return res
  120. def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> str:
  121. """
  122. API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/raw_content
  123. Example Response:
  124. {
  125. "code": 0,
  126. "msg": "success",
  127. "data": {
  128. "content": "云文档\n多人实时协同,插入一切元素。不仅是在线文档,更是强大的创作和互动工具\n云文档:专为协作而生\n"
  129. }
  130. }
  131. """ # noqa: E501
  132. params = {
  133. "document_id": document_id,
  134. "mode": mode,
  135. "lang": lang,
  136. }
  137. url = f"{self.API_BASE_URL}/document/get_document_content"
  138. res = self._send_request(url, method="GET", params=params)
  139. if "data" in res:
  140. return res.get("data").get("content")
  141. return ""
  142. def list_document_blocks(
  143. self, document_id: str, page_token: str, user_id_type: str = "open_id", page_size: int = 500
  144. ) -> dict:
  145. """
  146. API url: https://open.larkoffice.com/document/server-docs/docs/docs/docx-v1/document/list
  147. """
  148. params = {
  149. "user_id_type": user_id_type,
  150. "document_id": document_id,
  151. "page_size": page_size,
  152. "page_token": page_token,
  153. }
  154. url = f"{self.API_BASE_URL}/document/list_document_blocks"
  155. res = self._send_request(url, method="GET", params=params)
  156. if "data" in res:
  157. return res.get("data")
  158. return res
  159. def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict:
  160. """
  161. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/create
  162. """
  163. url = f"{self.API_BASE_URL}/message/send_bot_message"
  164. params = {
  165. "receive_id_type": receive_id_type,
  166. }
  167. payload = {
  168. "receive_id": receive_id,
  169. "msg_type": msg_type,
  170. "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
  171. }
  172. res = self._send_request(url, params=params, payload=payload)
  173. if "data" in res:
  174. return res.get("data")
  175. return res
  176. def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict:
  177. url = f"{self.API_BASE_URL}/message/send_webhook_message"
  178. payload = {
  179. "webhook": webhook,
  180. "msg_type": msg_type,
  181. "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"),
  182. }
  183. res = self._send_request(url, require_token=False, payload=payload)
  184. return res
  185. def get_chat_messages(
  186. self,
  187. container_id: str,
  188. start_time: str,
  189. end_time: str,
  190. page_token: str,
  191. sort_type: str = "ByCreateTimeAsc",
  192. page_size: int = 20,
  193. ) -> dict:
  194. """
  195. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
  196. """
  197. url = f"{self.API_BASE_URL}/message/get_chat_messages"
  198. params = {
  199. "container_id": container_id,
  200. "start_time": start_time,
  201. "end_time": end_time,
  202. "sort_type": sort_type,
  203. "page_token": page_token,
  204. "page_size": page_size,
  205. }
  206. res = self._send_request(url, method="GET", params=params)
  207. if "data" in res:
  208. return res.get("data")
  209. return res
  210. def get_thread_messages(
  211. self, container_id: str, page_token: str, sort_type: str = "ByCreateTimeAsc", page_size: int = 20
  212. ) -> dict:
  213. """
  214. API url: https://open.larkoffice.com/document/server-docs/im-v1/message/list
  215. """
  216. url = f"{self.API_BASE_URL}/message/get_thread_messages"
  217. params = {
  218. "container_id": container_id,
  219. "sort_type": sort_type,
  220. "page_token": page_token,
  221. "page_size": page_size,
  222. }
  223. res = self._send_request(url, method="GET", params=params)
  224. if "data" in res:
  225. return res.get("data")
  226. return res
  227. def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict:
  228. # 创建任务
  229. url = f"{self.API_BASE_URL}/task/create_task"
  230. payload = {
  231. "summary": summary,
  232. "start_time": start_time,
  233. "end_time": end_time,
  234. "completed_at": completed_time,
  235. "description": description,
  236. }
  237. res = self._send_request(url, payload=payload)
  238. if "data" in res:
  239. return res.get("data")
  240. return res
  241. def update_task(
  242. self, task_guid: str, summary: str, start_time: str, end_time: str, completed_time: str, description: str
  243. ) -> dict:
  244. # 更新任务
  245. url = f"{self.API_BASE_URL}/task/update_task"
  246. payload = {
  247. "task_guid": task_guid,
  248. "summary": summary,
  249. "start_time": start_time,
  250. "end_time": end_time,
  251. "completed_time": completed_time,
  252. "description": description,
  253. }
  254. res = self._send_request(url, method="PATCH", payload=payload)
  255. if "data" in res:
  256. return res.get("data")
  257. return res
  258. def delete_task(self, task_guid: str) -> dict:
  259. # 删除任务
  260. url = f"{self.API_BASE_URL}/task/delete_task"
  261. payload = {
  262. "task_guid": task_guid,
  263. }
  264. res = self._send_request(url, method="DELETE", payload=payload)
  265. return res
  266. def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict:
  267. # 删除任务
  268. url = f"{self.API_BASE_URL}/task/add_members"
  269. payload = {
  270. "task_guid": task_guid,
  271. "member_phone_or_email": member_phone_or_email,
  272. "member_role": member_role,
  273. }
  274. res = self._send_request(url, payload=payload)
  275. return res
  276. def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict:
  277. # 获取知识库全部子节点列表
  278. url = f"{self.API_BASE_URL}/wiki/get_wiki_nodes"
  279. payload = {
  280. "space_id": space_id,
  281. "parent_node_token": parent_node_token,
  282. "page_token": page_token,
  283. "page_size": page_size,
  284. }
  285. res = self._send_request(url, payload=payload)
  286. if "data" in res:
  287. return res.get("data")
  288. return res
  289. def get_primary_calendar(self, user_id_type: str = "open_id") -> dict:
  290. url = f"{self.API_BASE_URL}/calendar/get_primary_calendar"
  291. params = {
  292. "user_id_type": user_id_type,
  293. }
  294. res = self._send_request(url, method="GET", params=params)
  295. if "data" in res:
  296. return res.get("data")
  297. return res
  298. def create_event(
  299. self,
  300. summary: str,
  301. description: str,
  302. start_time: str,
  303. end_time: str,
  304. attendee_ability: str,
  305. need_notification: bool = True,
  306. auto_record: bool = False,
  307. ) -> dict:
  308. url = f"{self.API_BASE_URL}/calendar/create_event"
  309. payload = {
  310. "summary": summary,
  311. "description": description,
  312. "need_notification": need_notification,
  313. "start_time": start_time,
  314. "end_time": end_time,
  315. "auto_record": auto_record,
  316. "attendee_ability": attendee_ability,
  317. }
  318. res = self._send_request(url, payload=payload)
  319. if "data" in res:
  320. return res.get("data")
  321. return res
  322. def update_event(
  323. self,
  324. event_id: str,
  325. summary: str,
  326. description: str,
  327. need_notification: bool,
  328. start_time: str,
  329. end_time: str,
  330. auto_record: bool,
  331. ) -> dict:
  332. url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}"
  333. payload = {}
  334. if summary:
  335. payload["summary"] = summary
  336. if description:
  337. payload["description"] = description
  338. if start_time:
  339. payload["start_time"] = start_time
  340. if end_time:
  341. payload["end_time"] = end_time
  342. if need_notification:
  343. payload["need_notification"] = need_notification
  344. if auto_record:
  345. payload["auto_record"] = auto_record
  346. res = self._send_request(url, method="PATCH", payload=payload)
  347. return res
  348. def delete_event(self, event_id: str, need_notification: bool = True) -> dict:
  349. url = f"{self.API_BASE_URL}/calendar/delete_event/{event_id}"
  350. params = {
  351. "need_notification": need_notification,
  352. }
  353. res = self._send_request(url, method="DELETE", params=params)
  354. return res
  355. def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict:
  356. url = f"{self.API_BASE_URL}/calendar/list_events"
  357. params = {
  358. "start_time": start_time,
  359. "end_time": end_time,
  360. "page_token": page_token,
  361. "page_size": page_size,
  362. }
  363. res = self._send_request(url, method="GET", params=params)
  364. if "data" in res:
  365. return res.get("data")
  366. return res
  367. def search_events(
  368. self,
  369. query: str,
  370. start_time: str,
  371. end_time: str,
  372. page_token: str,
  373. user_id_type: str = "open_id",
  374. page_size: int = 20,
  375. ) -> dict:
  376. url = f"{self.API_BASE_URL}/calendar/search_events"
  377. payload = {
  378. "query": query,
  379. "start_time": start_time,
  380. "end_time": end_time,
  381. "page_token": page_token,
  382. "user_id_type": user_id_type,
  383. "page_size": page_size,
  384. }
  385. res = self._send_request(url, payload=payload)
  386. if "data" in res:
  387. return res.get("data")
  388. return res
  389. def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict:
  390. # 参加日程参会人
  391. url = f"{self.API_BASE_URL}/calendar/add_event_attendees"
  392. payload = {
  393. "event_id": event_id,
  394. "attendee_phone_or_email": attendee_phone_or_email,
  395. "need_notification": need_notification,
  396. }
  397. res = self._send_request(url, payload=payload)
  398. if "data" in res:
  399. return res.get("data")
  400. return res
  401. def create_spreadsheet(
  402. self,
  403. title: str,
  404. folder_token: str,
  405. ) -> dict:
  406. # 创建电子表格
  407. url = f"{self.API_BASE_URL}/spreadsheet/create_spreadsheet"
  408. payload = {
  409. "title": title,
  410. "folder_token": folder_token,
  411. }
  412. res = self._send_request(url, payload=payload)
  413. if "data" in res:
  414. return res.get("data")
  415. return res
  416. def get_spreadsheet(
  417. self,
  418. spreadsheet_token: str,
  419. user_id_type: str = "open_id",
  420. ) -> dict:
  421. # 获取电子表格信息
  422. url = f"{self.API_BASE_URL}/spreadsheet/get_spreadsheet"
  423. params = {
  424. "spreadsheet_token": spreadsheet_token,
  425. "user_id_type": user_id_type,
  426. }
  427. res = self._send_request(url, method="GET", params=params)
  428. if "data" in res:
  429. return res.get("data")
  430. return res
  431. def list_spreadsheet_sheets(
  432. self,
  433. spreadsheet_token: str,
  434. ) -> dict:
  435. # 列出电子表格的所有工作表
  436. url = f"{self.API_BASE_URL}/spreadsheet/list_spreadsheet_sheets"
  437. params = {
  438. "spreadsheet_token": spreadsheet_token,
  439. }
  440. res = self._send_request(url, method="GET", params=params)
  441. if "data" in res:
  442. return res.get("data")
  443. return res
  444. def add_rows(
  445. self,
  446. spreadsheet_token: str,
  447. sheet_id: str,
  448. sheet_name: str,
  449. length: int,
  450. values: str,
  451. ) -> dict:
  452. # 增加行,在工作表最后添加
  453. url = f"{self.API_BASE_URL}/spreadsheet/add_rows"
  454. payload = {
  455. "spreadsheet_token": spreadsheet_token,
  456. "sheet_id": sheet_id,
  457. "sheet_name": sheet_name,
  458. "length": length,
  459. "values": values,
  460. }
  461. res = self._send_request(url, payload=payload)
  462. if "data" in res:
  463. return res.get("data")
  464. return res
  465. def add_cols(
  466. self,
  467. spreadsheet_token: str,
  468. sheet_id: str,
  469. sheet_name: str,
  470. length: int,
  471. values: str,
  472. ) -> dict:
  473. # 增加列,在工作表最后添加
  474. url = f"{self.API_BASE_URL}/spreadsheet/add_cols"
  475. payload = {
  476. "spreadsheet_token": spreadsheet_token,
  477. "sheet_id": sheet_id,
  478. "sheet_name": sheet_name,
  479. "length": length,
  480. "values": values,
  481. }
  482. res = self._send_request(url, payload=payload)
  483. if "data" in res:
  484. return res.get("data")
  485. return res
  486. def read_rows(
  487. self,
  488. spreadsheet_token: str,
  489. sheet_id: str,
  490. sheet_name: str,
  491. start_row: int,
  492. num_rows: int,
  493. user_id_type: str = "open_id",
  494. ) -> dict:
  495. # 读取工作表行数据
  496. url = f"{self.API_BASE_URL}/spreadsheet/read_rows"
  497. params = {
  498. "spreadsheet_token": spreadsheet_token,
  499. "sheet_id": sheet_id,
  500. "sheet_name": sheet_name,
  501. "start_row": start_row,
  502. "num_rows": num_rows,
  503. "user_id_type": user_id_type,
  504. }
  505. res = self._send_request(url, method="GET", params=params)
  506. if "data" in res:
  507. return res.get("data")
  508. return res
  509. def read_cols(
  510. self,
  511. spreadsheet_token: str,
  512. sheet_id: str,
  513. sheet_name: str,
  514. start_col: int,
  515. num_cols: int,
  516. user_id_type: str = "open_id",
  517. ) -> dict:
  518. # 读取工作表列数据
  519. url = f"{self.API_BASE_URL}/spreadsheet/read_cols"
  520. params = {
  521. "spreadsheet_token": spreadsheet_token,
  522. "sheet_id": sheet_id,
  523. "sheet_name": sheet_name,
  524. "start_col": start_col,
  525. "num_cols": num_cols,
  526. "user_id_type": user_id_type,
  527. }
  528. res = self._send_request(url, method="GET", params=params)
  529. if "data" in res:
  530. return res.get("data")
  531. return res
  532. def read_table(
  533. self,
  534. spreadsheet_token: str,
  535. sheet_id: str,
  536. sheet_name: str,
  537. num_range: str,
  538. query: str,
  539. user_id_type: str = "open_id",
  540. ) -> dict:
  541. # 自定义读取行列数据
  542. url = f"{self.API_BASE_URL}/spreadsheet/read_table"
  543. params = {
  544. "spreadsheet_token": spreadsheet_token,
  545. "sheet_id": sheet_id,
  546. "sheet_name": sheet_name,
  547. "range": num_range,
  548. "query": query,
  549. "user_id_type": user_id_type,
  550. }
  551. res = self._send_request(url, method="GET", params=params)
  552. if "data" in res:
  553. return res.get("data")
  554. return res
  555. def create_base(
  556. self,
  557. name: str,
  558. folder_token: str,
  559. ) -> dict:
  560. # 创建多维表格
  561. url = f"{self.API_BASE_URL}/base/create_base"
  562. payload = {
  563. "name": name,
  564. "folder_token": folder_token,
  565. }
  566. res = self._send_request(url, payload=payload)
  567. if "data" in res:
  568. return res.get("data")
  569. return res
  570. def add_records(
  571. self,
  572. app_token: str,
  573. table_id: str,
  574. table_name: str,
  575. records: str,
  576. user_id_type: str = "open_id",
  577. ) -> dict:
  578. # 新增多条记录
  579. url = f"{self.API_BASE_URL}/base/add_records"
  580. params = {
  581. "app_token": app_token,
  582. "table_id": table_id,
  583. "table_name": table_name,
  584. "user_id_type": user_id_type,
  585. }
  586. payload = {
  587. "records": convert_add_records(records),
  588. }
  589. res = self._send_request(url, params=params, payload=payload)
  590. if "data" in res:
  591. return res.get("data")
  592. return res
  593. def update_records(
  594. self,
  595. app_token: str,
  596. table_id: str,
  597. table_name: str,
  598. records: str,
  599. user_id_type: str,
  600. ) -> dict:
  601. # 更新多条记录
  602. url = f"{self.API_BASE_URL}/base/update_records"
  603. params = {
  604. "app_token": app_token,
  605. "table_id": table_id,
  606. "table_name": table_name,
  607. "user_id_type": user_id_type,
  608. }
  609. payload = {
  610. "records": convert_update_records(records),
  611. }
  612. res = self._send_request(url, params=params, payload=payload)
  613. if "data" in res:
  614. return res.get("data")
  615. return res
  616. def delete_records(
  617. self,
  618. app_token: str,
  619. table_id: str,
  620. table_name: str,
  621. record_ids: str,
  622. ) -> dict:
  623. # 删除多条记录
  624. url = f"{self.API_BASE_URL}/base/delete_records"
  625. params = {
  626. "app_token": app_token,
  627. "table_id": table_id,
  628. "table_name": table_name,
  629. }
  630. if not record_ids:
  631. record_id_list = []
  632. else:
  633. try:
  634. record_id_list = json.loads(record_ids)
  635. except json.JSONDecodeError:
  636. raise ValueError("The input string is not valid JSON")
  637. payload = {
  638. "records": record_id_list,
  639. }
  640. res = self._send_request(url, params=params, payload=payload)
  641. if "data" in res:
  642. return res.get("data")
  643. return res
  644. def search_record(
  645. self,
  646. app_token: str,
  647. table_id: str,
  648. table_name: str,
  649. view_id: str,
  650. field_names: str,
  651. sort: str,
  652. filters: str,
  653. page_token: str,
  654. automatic_fields: bool = False,
  655. user_id_type: str = "open_id",
  656. page_size: int = 20,
  657. ) -> dict:
  658. # 查询记录,单次最多查询 500 行记录。
  659. url = f"{self.API_BASE_URL}/base/search_record"
  660. params = {
  661. "app_token": app_token,
  662. "table_id": table_id,
  663. "table_name": table_name,
  664. "user_id_type": user_id_type,
  665. "page_token": page_token,
  666. "page_size": page_size,
  667. }
  668. if not field_names:
  669. field_name_list = []
  670. else:
  671. try:
  672. field_name_list = json.loads(field_names)
  673. except json.JSONDecodeError:
  674. raise ValueError("The input string is not valid JSON")
  675. if not sort:
  676. sort_list = []
  677. else:
  678. try:
  679. sort_list = json.loads(sort)
  680. except json.JSONDecodeError:
  681. raise ValueError("The input string is not valid JSON")
  682. if not filters:
  683. filter_dict = {}
  684. else:
  685. try:
  686. filter_dict = json.loads(filters)
  687. except json.JSONDecodeError:
  688. raise ValueError("The input string is not valid JSON")
  689. payload = {}
  690. if view_id:
  691. payload["view_id"] = view_id
  692. if field_names:
  693. payload["field_names"] = field_name_list
  694. if sort:
  695. payload["sort"] = sort_list
  696. if filters:
  697. payload["filter"] = filter_dict
  698. if automatic_fields:
  699. payload["automatic_fields"] = automatic_fields
  700. res = self._send_request(url, params=params, payload=payload)
  701. if "data" in res:
  702. return res.get("data")
  703. return res
  704. def get_base_info(
  705. self,
  706. app_token: str,
  707. ) -> dict:
  708. # 获取多维表格元数据
  709. url = f"{self.API_BASE_URL}/base/get_base_info"
  710. params = {
  711. "app_token": app_token,
  712. }
  713. res = self._send_request(url, method="GET", params=params)
  714. if "data" in res:
  715. return res.get("data")
  716. return res
  717. def create_table(
  718. self,
  719. app_token: str,
  720. table_name: str,
  721. default_view_name: str,
  722. fields: str,
  723. ) -> dict:
  724. # 新增一个数据表
  725. url = f"{self.API_BASE_URL}/base/create_table"
  726. params = {
  727. "app_token": app_token,
  728. }
  729. if not fields:
  730. fields_list = []
  731. else:
  732. try:
  733. fields_list = json.loads(fields)
  734. except json.JSONDecodeError:
  735. raise ValueError("The input string is not valid JSON")
  736. payload = {
  737. "name": table_name,
  738. "fields": fields_list,
  739. }
  740. if default_view_name:
  741. payload["default_view_name"] = default_view_name
  742. res = self._send_request(url, params=params, payload=payload)
  743. if "data" in res:
  744. return res.get("data")
  745. return res
  746. def delete_tables(
  747. self,
  748. app_token: str,
  749. table_ids: str,
  750. table_names: str,
  751. ) -> dict:
  752. # 删除多个数据表
  753. url = f"{self.API_BASE_URL}/base/delete_tables"
  754. params = {
  755. "app_token": app_token,
  756. }
  757. if not table_ids:
  758. table_id_list = []
  759. else:
  760. try:
  761. table_id_list = json.loads(table_ids)
  762. except json.JSONDecodeError:
  763. raise ValueError("The input string is not valid JSON")
  764. if not table_names:
  765. table_name_list = []
  766. else:
  767. try:
  768. table_name_list = json.loads(table_names)
  769. except json.JSONDecodeError:
  770. raise ValueError("The input string is not valid JSON")
  771. payload = {
  772. "table_ids": table_id_list,
  773. "table_names": table_name_list,
  774. }
  775. res = self._send_request(url, params=params, payload=payload)
  776. if "data" in res:
  777. return res.get("data")
  778. return res
  779. def list_tables(
  780. self,
  781. app_token: str,
  782. page_token: str,
  783. page_size: int = 20,
  784. ) -> dict:
  785. # 列出多维表格下的全部数据表
  786. url = f"{self.API_BASE_URL}/base/list_tables"
  787. params = {
  788. "app_token": app_token,
  789. "page_token": page_token,
  790. "page_size": page_size,
  791. }
  792. res = self._send_request(url, method="GET", params=params)
  793. if "data" in res:
  794. return res.get("data")
  795. return res
  796. def read_records(
  797. self,
  798. app_token: str,
  799. table_id: str,
  800. table_name: str,
  801. record_ids: str,
  802. user_id_type: str = "open_id",
  803. ) -> dict:
  804. url = f"{self.API_BASE_URL}/base/read_records"
  805. params = {
  806. "app_token": app_token,
  807. "table_id": table_id,
  808. "table_name": table_name,
  809. }
  810. if not record_ids:
  811. record_id_list = []
  812. else:
  813. try:
  814. record_id_list = json.loads(record_ids)
  815. except json.JSONDecodeError:
  816. raise ValueError("The input string is not valid JSON")
  817. payload = {
  818. "record_ids": record_id_list,
  819. "user_id_type": user_id_type,
  820. }
  821. res = self._send_request(url, method="GET", params=params, payload=payload)
  822. if "data" in res:
  823. return res.get("data")
  824. return res