from __future__ import annotations import logging import re from yuxi.channel.extensions.feishu.client import get_client logger = logging.getLogger(__name__) FEISHU_BITABLE_TOOLS = [ { "name": "feishu_bitable_tables", "description": "列出多维表格中的数据表", "parameters": {"app_token": "多维表格 app_token"}, }, { "name": "feishu_bitable_fields", "description": "列出数据表的字段", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID"}, }, { "name": "feishu_bitable_query", "description": "查询多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID"}, }, { "name": "feishu_bitable_create", "description": "创建多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID", "fields": "字段值字典"}, }, { "name": "feishu_bitable_update", "description": "更新多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID", "record_id": "记录 ID", "fields": "字段值字典"}, }, { "name": "feishu_bitable_delete", "description": "删除多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID", "record_id": "记录 ID"}, }, { "name": "feishu_bitable_batch_create", "description": "批量创建多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID", "records": "记录列表"}, }, { "name": "feishu_bitable_batch_update", "description": "批量更新多维表格记录", "parameters": {"app_token": "多维表格 token", "table_id": "数据表 ID", "records": "记录列表"}, }, ] BITABLE_URL_PATTERN = re.compile( r"(?:feishu|lark)\.(?:cn|suite)\.com/(?:base|wiki/base)/([A-Za-z0-9]+)(?:\?.*table=([A-Za-z0-9]+))?" ) def parse_bitable_url(url: str) -> dict | None: match = BITABLE_URL_PATTERN.search(url) if match: result = {"app_token": match.group(1)} if match.group(2): result["table_id"] = match.group(2) return result return None async def list_bitable_tables( app_id: str, app_secret: str, domain: str, app_token: str, http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.ListAppTableRequest(app_token=app_token, page_size=50) resp = await client.bitable.v1.app_table.list_async(req) if resp.success() and resp.data: items = getattr(resp.data, "items", []) or [] return { "success": True, "tables": [ { "table_id": getattr(t, "table_id", ""), "name": getattr(t, "name", ""), "revision": getattr(t, "revision", 0), } for t in items ], } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu list_bitable_tables error") return {"success": False, "error": "Internal error"} async def list_bitable_fields( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.ListAppTableFieldRequest( app_token=app_token, table_id=table_id, page_size=100, ) resp = await client.bitable.v1.app_table_field.list_async(req) if resp.success() and resp.data: items = getattr(resp.data, "items", []) or [] return { "success": True, "fields": [ { "field_id": getattr(f, "field_id", ""), "field_name": getattr(f, "field_name", ""), "type": getattr(f, "type", 0), } for f in items ], } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu list_bitable_fields error") return {"success": False, "error": "Internal error"} async def query_bitable_records( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, *, page_size: int = 100, page_token: str = "", http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.ListAppTableRecordRequest( app_token=app_token, table_id=table_id, page_size=page_size, page_token=page_token if page_token else None, ) resp = await client.bitable.v1.app_table_record.list_async(req) if resp.success() and resp.data: items = getattr(resp.data, "items", []) or [] return { "success": True, "records": [ { "record_id": getattr(r, "record_id", ""), "fields": getattr(r, "fields", {}), } for r in items ], "has_more": getattr(resp.data, "has_more", False), "page_token": getattr(resp.data, "page_token", ""), } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu query_bitable_records error") return {"success": False, "error": "Internal error"} async def create_bitable_record( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, fields: dict, http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.CreateAppTableRecordRequest( app_token=app_token, table_id=table_id, request_body=lark_oapi.bitable.v1.CreateAppTableRecordRequestBody( fields=fields, ), ) resp = await client.bitable.v1.app_table_record.create_async(req) if resp.success() and resp.data: return { "success": True, "record": { "record_id": getattr(resp.data.record, "record_id", ""), "fields": getattr(resp.data.record, "fields", {}), }, } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu create_bitable_record error") return {"success": False, "error": "Internal error"} async def update_bitable_record( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, record_id: str, fields: dict, http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.UpdateAppTableRecordRequest( app_token=app_token, table_id=table_id, record_id=record_id, request_body=lark_oapi.bitable.v1.UpdateAppTableRecordRequestBody( fields=fields, ), ) resp = await client.bitable.v1.app_table_record.update_async(req) if resp.success() and resp.data: return { "success": True, "record": { "record_id": getattr(resp.data.record, "record_id", ""), "fields": getattr(resp.data.record, "fields", {}), }, } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu update_bitable_record error") return {"success": False, "error": "Internal error"} async def delete_bitable_record( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, record_id: str, http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.DeleteAppTableRecordRequest( app_token=app_token, table_id=table_id, record_id=record_id, ) resp = await client.bitable.v1.app_table_record.delete_async(req) if resp.success(): return {"success": True} else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu delete_bitable_record error") return {"success": False, "error": "Internal error"} async def batch_create_bitable_records( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, records: list[dict], http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.CreateAppTableRecordRequest( app_token=app_token, table_id=table_id, request_body=lark_oapi.bitable.v1.BatchCreateAppTableRecordRequestBody( records=[ lark_oapi.bitable.v1.AppTableRecord(fields=r.get("fields", {})) for r in records ], ), ) resp = await client.bitable.v1.app_table_record.batch_create_async(req) if resp.success() and resp.data: items = getattr(resp.data, "records", []) or [] return { "success": True, "records": [ { "record_id": getattr(r, "record_id", ""), "fields": getattr(r, "fields", {}), } for r in items ], } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu batch_create_bitable_records error") return {"success": False, "error": "Internal error"} async def batch_update_bitable_records( app_id: str, app_secret: str, domain: str, app_token: str, table_id: str, records: list[dict], http_timeout_ms: int = 30000, ) -> dict: try: client = get_client(app_id, app_secret, domain, http_timeout_ms) import lark_oapi req = lark_oapi.bitable.v1.BatchUpdateAppTableRecordRequest( app_token=app_token, table_id=table_id, request_body=lark_oapi.bitable.v1.BatchUpdateAppTableRecordRequestBody( records=[ lark_oapi.bitable.v1.AppTableRecord( record_id=r.get("record_id", ""), fields=r.get("fields", {}), ) for r in records ], ), ) resp = await client.bitable.v1.app_table_record.batch_update_async(req) if resp.success() and resp.data: items = getattr(resp.data, "records", []) or [] return { "success": True, "records": [ { "record_id": getattr(r, "record_id", ""), "fields": getattr(r, "fields", {}), } for r in items ], } else: return {"success": False, "error": getattr(resp, "msg", "")} except ImportError: return {"success": False, "error": "lark-oapi SDK not installed"} except Exception: logger.exception("Feishu batch_update_bitable_records error") return {"success": False, "error": "Internal error"}