sophhub 0.2.2 → 0.2.4
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.
- package/package.json +1 -1
- package/skills/consensus/skill.json +20 -0
- package/skills/consensus/src/SKILL.md +93 -0
- package/skills/deepwiki/skill.json +20 -0
- package/skills/deepwiki/src/SKILL.md +45 -0
- package/skills/deepwiki/src/_meta.json +6 -0
- package/skills/deepwiki/src/scripts/deepwiki.js +135 -0
- package/skills/feishu-bitable/skill.json +20 -0
- package/skills/feishu-bitable/src/CHECKLIST.md +150 -0
- package/skills/feishu-bitable/src/README.md +178 -0
- package/skills/feishu-bitable/src/SKILL.md +113 -0
- package/skills/feishu-bitable/src/_meta.json +6 -0
- package/skills/feishu-bitable/src/api.js +381 -0
- package/skills/feishu-bitable/src/bin/cli.js +284 -0
- package/skills/feishu-bitable/src/description.md +143 -0
- package/skills/feishu-bitable/src/examples/create-records.json +52 -0
- package/skills/feishu-bitable/src/examples/create-table.json +64 -0
- package/skills/feishu-bitable/src/package-lock.json +324 -0
- package/skills/feishu-bitable/src/package.json +33 -0
- package/skills/feishu-bitable/src/publish-config.json +14 -0
- package/skills/feishu-bitable/src/test-simple.js +61 -0
- package/skills/feishu-bitable/src/utils.js +261 -0
- package/skills/flight-booking/skill.json +9 -2
- package/skills/flight-booking/src/scripts/flight_booking.py +2 -1
- package/skills/google-maps/skill.json +20 -0
- package/skills/google-maps/src/SKILL.md +237 -0
- package/skills/google-maps/src/_meta.json +6 -0
- package/skills/google-maps/src/lib/map_helper.py +912 -0
- package/skills/large-task-router/skill.json +20 -0
- package/skills/large-task-router/src/SKILL.md +79 -0
- package/skills/large-task-router/src/templates/plan.md +74 -0
- package/skills/skillhub/skill.json +11 -4
- package/skills/skillhub/src/SKILL.md +11 -1
- package/skills/sophnet-dailynews/skill.json +20 -0
- package/skills/sophnet-dailynews/src/SKILL.md +179 -0
- package/skills/sophnet-dailynews/src/cache.json +151 -0
- package/skills/sophnet-dailynews/src/sources.json +230 -0
- package/skills/sophnet-schedule/skill.json +20 -0
- package/skills/sophnet-schedule/src/ARCHITECTURE.md +321 -0
- package/skills/sophnet-schedule/src/IMPROVEMENTS.md +145 -0
- package/skills/sophnet-schedule/src/SKILL.md +1050 -0
- package/skills/sophnet-schedule/src/_meta.json +6 -0
- package/skills/sophnet-schedule/src/api/__init__.py +0 -0
- package/skills/sophnet-schedule/src/api/models.py +245 -0
- package/skills/sophnet-schedule/src/apps/add_event.py +237 -0
- package/skills/sophnet-schedule/src/apps/check_reminders.py +112 -0
- package/skills/sophnet-schedule/src/apps/check_roc.py +246 -0
- package/skills/sophnet-schedule/src/apps/generate_daily_plan.py +342 -0
- package/skills/sophnet-schedule/src/apps/import_events.py +216 -0
- package/skills/sophnet-schedule/src/apps/monitor_calendar_changes.py +140 -0
- package/skills/sophnet-schedule/src/apps/register_tasks.py +169 -0
- package/skills/sophnet-schedule/src/apps/sync_roc_to_gcal.py +174 -0
- package/skills/sophnet-schedule/src/compat.py +66 -0
- package/skills/sophnet-schedule/src/config/__init__.py +0 -0
- package/skills/sophnet-schedule/src/config/reminder_rules.yaml +96 -0
- package/skills/sophnet-schedule/src/config/roc_events.yaml +44 -0
- package/skills/sophnet-schedule/src/config/settings.py +133 -0
- package/skills/sophnet-schedule/src/config/task_registry.yaml +92 -0
- package/skills/sophnet-schedule/src/docs/FRONTEND_INTEGRATION_GUIDE.md +437 -0
- package/skills/sophnet-schedule/src/gcal/__init__.py +0 -0
- package/skills/sophnet-schedule/src/gcal/client.py +374 -0
- package/skills/sophnet-schedule/src/gcal/models.py +91 -0
- package/skills/sophnet-schedule/src/requirements.txt +6 -0
- package/skills/sophnet-schedule/src/scripts/setup_gcal_token.py +85 -0
- package/skills/sophnet-schedule/src/server.py +669 -0
- package/skills/sophnet-schedule/src/services/__init__.py +0 -0
- package/skills/sophnet-schedule/src/services/calendar_backend.py +139 -0
- package/skills/sophnet-schedule/src/services/conflict_detector.py +96 -0
- package/skills/sophnet-schedule/src/services/datetime_utils.py +117 -0
- package/skills/sophnet-schedule/src/services/event_classifier.py +100 -0
- package/skills/sophnet-schedule/src/services/event_diff.py +160 -0
- package/skills/sophnet-schedule/src/services/google_integration.py +500 -0
- package/skills/sophnet-schedule/src/services/job_store.py +100 -0
- package/skills/sophnet-schedule/src/services/local_event_store.py +266 -0
- package/skills/sophnet-schedule/src/services/reminder_planner.py +116 -0
- package/skills/sophnet-schedule/src/services/runtime_utils.py +31 -0
- package/skills/sophnet-schedule/src/services/table_parser.py +286 -0
- package/skills/sophnet-schedule/src/services/task_builder.py +167 -0
- package/skills/sophnet-schedule/src/services/time_window.py +72 -0
- package/skills/sophnet-stock/skill.json +20 -0
- package/skills/sophnet-stock/src/App-Plan.md +442 -0
- package/skills/sophnet-stock/src/README.md +214 -0
- package/skills/sophnet-stock/src/SKILL.md +236 -0
- package/skills/sophnet-stock/src/TODO.md +394 -0
- package/skills/sophnet-stock/src/_meta.json +6 -0
- package/skills/sophnet-stock/src/docs/ARCHITECTURE.md +408 -0
- package/skills/sophnet-stock/src/docs/CONCEPT.md +233 -0
- package/skills/sophnet-stock/src/docs/HOT_SCANNER.md +288 -0
- package/skills/sophnet-stock/src/docs/README.md +95 -0
- package/skills/sophnet-stock/src/docs/USAGE.md +465 -0
- package/skills/sophnet-stock/src/scripts/analyze_stock.py +2565 -0
- package/skills/sophnet-stock/src/scripts/dividends.py +365 -0
- package/skills/sophnet-stock/src/scripts/hot_scanner.py +582 -0
- package/skills/sophnet-stock/src/scripts/portfolio.py +548 -0
- package/skills/sophnet-stock/src/scripts/rumor_scanner.py +342 -0
- package/skills/sophnet-stock/src/scripts/test_stock_analysis.py +409 -0
- package/skills/sophnet-stock/src/scripts/watchlist.py +336 -0
- package/skills/xiaohongshu/skill.json +20 -0
- package/skills/xiaohongshu/src/SKILL.md +91 -0
- package/skills/xiaohongshu/src/_meta.json +6 -0
- package/skills/xiaohongshu/src/assets/card.html +216 -0
- package/skills/xiaohongshu/src/assets/cover.html +82 -0
- package/skills/xiaohongshu/src/assets/example.md +84 -0
- package/skills/xiaohongshu/src/assets/styles.css +318 -0
- package/skills/xiaohongshu/src/scripts/render_xhs_v2.py +737 -0
- package/skills/xiaohongshu/src/scripts/sign_server.py +158 -0
- package/skills/xiaohongshu/src/scripts/stealth.min.js +7 -0
- package/skills/xiaohongshu/src/scripts/xhs_tool.py +186 -0
- package/skills/xiaohongshu/src/workflow.py +185 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feishu-bitable
|
|
3
|
+
description: CRUD operations for Feishu Bitable (multidimensional tables). Use when the user needs to manage tables, records, fields, or views in Feishu Bitable, or when dealing with bitable-related URLs.
|
|
4
|
+
metadata: {"openclaw":{"emoji":"📊","requires":{"env":["FEISHU_APP_ID","FEISHU_APP_SECRET"]},"primaryEnv":"FEISHU_APP_ID"}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Feishu Bitable Skill
|
|
8
|
+
|
|
9
|
+
CRUD operations for Feishu Bitable (多维表格) — tables, records, fields, and views.
|
|
10
|
+
|
|
11
|
+
## Environment Variables
|
|
12
|
+
|
|
13
|
+
`FEISHU_APP_ID` and `FEISHU_APP_SECRET` are already available from the Feishu channel configuration. No extra setup is needed. The CLI reads them automatically via `dotenv` or shell environment.
|
|
14
|
+
|
|
15
|
+
## Extracting app-token from URLs
|
|
16
|
+
|
|
17
|
+
Feishu Bitable URLs come in three formats. Extract the `app-token` as follows:
|
|
18
|
+
|
|
19
|
+
| URL Format | Example | app-token |
|
|
20
|
+
|-----------|---------|-----------|
|
|
21
|
+
| **Wiki /wiki/** | `feishu.cn/wiki/AbCdEf?table=tblXXX` | Wiki token `AbCdEf` can be used directly as app-token (Feishu API accepts it) |
|
|
22
|
+
| **Standalone /base/** | `feishu.cn/base/basXXXXXX` | `basXXXXXX` |
|
|
23
|
+
| **Advanced /app/** | `feishu.cn/app/AbCdEf?pageId=pgeXXX` | `AbCdEf` |
|
|
24
|
+
|
|
25
|
+
The `table=tblXXX` query parameter is the `table-id`. The `view=vewXXX` parameter is the view-id.
|
|
26
|
+
|
|
27
|
+
**Note on /wiki/ URLs:** Feishu Bitable API accepts wiki tokens directly. If user provides a /wiki/ URL with `table=` parameter, you can safely pass the wiki token as `--app-token`. If unsure whether the URL is a bitable, use `feishu_wiki` to check `obj_type` first.
|
|
28
|
+
|
|
29
|
+
## CLI Reference
|
|
30
|
+
|
|
31
|
+
All commands are run from the skill directory:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd {baseDir}
|
|
35
|
+
node bin/cli.js <command> [options]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Commands
|
|
39
|
+
|
|
40
|
+
| Command | Required Options | Optional | Description |
|
|
41
|
+
|---------|-----------------|----------|-------------|
|
|
42
|
+
| `test` | — | — | Test connection and token validity |
|
|
43
|
+
| `get-app` | `--app-token` | — | Get bitable app info |
|
|
44
|
+
| `list-tables` | `--app-token` | `--page-size`, `--page-token` | List all tables |
|
|
45
|
+
| `list-fields` | `--app-token --table-id` | `--page-size` | List fields in a table |
|
|
46
|
+
| `list-records` | `--app-token --table-id` | `--page-size`, `--page-token`, `--filter`, `--sort` | List records |
|
|
47
|
+
| `list-views` | `--app-token --table-id` | `--page-size` | List views |
|
|
48
|
+
| `create-table` | `--app-token --name` | `--fields <json>` | Create a new table |
|
|
49
|
+
| `create-record` | `--app-token --table-id --data <json>` | — | Create one record |
|
|
50
|
+
| `batch-create` | `--app-token --table-id --data <json-array>` | — | Batch create records (max 100) |
|
|
51
|
+
| `update-record` | `--app-token --table-id --record-id --data <json>` | — | Update a record |
|
|
52
|
+
| `delete-record` | `--app-token --table-id --record-id` | — | Delete a record |
|
|
53
|
+
|
|
54
|
+
### Data format
|
|
55
|
+
|
|
56
|
+
The `--data` option accepts inline JSON or a file reference with `@` prefix:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Inline JSON
|
|
60
|
+
--data '{"任务名称": "新任务", "状态": "待办"}'
|
|
61
|
+
|
|
62
|
+
# From file
|
|
63
|
+
--data @examples/create-records.json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For `batch-create`, `--data` must be a JSON array of record objects.
|
|
67
|
+
|
|
68
|
+
### Typical workflow
|
|
69
|
+
|
|
70
|
+
1. Parse the user's Feishu URL to extract `app-token` and `table-id`
|
|
71
|
+
2. `list-tables` → confirm the target table exists
|
|
72
|
+
3. `list-fields` → understand the table schema (field names and types)
|
|
73
|
+
4. Perform the requested operation (list/create/update/delete records)
|
|
74
|
+
5. Verify the result by re-reading if needed
|
|
75
|
+
|
|
76
|
+
## Field Types
|
|
77
|
+
|
|
78
|
+
| Type | API Value | Example Data |
|
|
79
|
+
|------|-----------|-------------|
|
|
80
|
+
| Text | 1 | `"Hello"` |
|
|
81
|
+
| Number | 2 | `123.45` |
|
|
82
|
+
| SingleSelect | 3 | `"选项名"` |
|
|
83
|
+
| MultiSelect | 4 | `["A", "B"]` |
|
|
84
|
+
| DateTime | 5 | Unix timestamp in ms: `1700000000000` |
|
|
85
|
+
| Checkbox | 7 | `true` / `false` |
|
|
86
|
+
| User | 11 | `[{"id": "ou_xxx"}]` |
|
|
87
|
+
| URL | 15 | `"https://..."` |
|
|
88
|
+
| Formula | 20 | Read-only, computed |
|
|
89
|
+
|
|
90
|
+
## Error Codes
|
|
91
|
+
|
|
92
|
+
| Code | Meaning | Action |
|
|
93
|
+
|------|---------|--------|
|
|
94
|
+
| 1254003 | WrongBaseToken | Check app-token format |
|
|
95
|
+
| 1254004 | WrongTableId | Verify table-id exists via `list-tables` |
|
|
96
|
+
| 1254041 | TableIdNotFound | Table does not exist |
|
|
97
|
+
| 1254043 | RecordIdNotFound | Record does not exist |
|
|
98
|
+
| 403 | Forbidden | App needs to be added as bitable collaborator with edit permission |
|
|
99
|
+
|
|
100
|
+
## Collaboration with Other Skills
|
|
101
|
+
|
|
102
|
+
- **File operations** (move/delete bitable): Use `feishu_drive` instead
|
|
103
|
+
- **Sharing/permissions**: Use `feishu_perm` instead
|
|
104
|
+
- **Wiki navigation**: Use `feishu_wiki` to discover file type from /wiki/ URLs when ambiguous
|
|
105
|
+
- **Spreadsheets**: If user mentions 单元格/cell/行列/row/column/A1, use `feishu-sheets` instead
|
|
106
|
+
- **Documents**: If user wants to edit text/paragraphs, use `feishu_doc` instead
|
|
107
|
+
|
|
108
|
+
## Notes
|
|
109
|
+
|
|
110
|
+
- Max 100 records per batch request
|
|
111
|
+
- Field names must exactly match the column names in the bitable
|
|
112
|
+
- DateTime fields use Unix timestamps in milliseconds
|
|
113
|
+
- Formula fields are read-only
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书多维表格API客户端
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const axios = require('axios');
|
|
6
|
+
const { getEnvVar, getAccessToken, validateRequired, parseJsonInput, delay } = require('./utils');
|
|
7
|
+
|
|
8
|
+
class FeishuBitableAPI {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.appId = options.appId || getEnvVar('FEISHU_APP_ID');
|
|
11
|
+
this.appSecret = options.appSecret || getEnvVar('FEISHU_APP_SECRET') || getEnvVar('FEISHU_APP_SECRET_PATH');
|
|
12
|
+
this.baseURL = options.baseURL || 'https://open.feishu.cn/open-apis/bitable/v1';
|
|
13
|
+
this.accessToken = options.accessToken;
|
|
14
|
+
this.autoRefreshToken = options.autoRefreshToken !== false;
|
|
15
|
+
|
|
16
|
+
if (!this.appId || !this.appSecret) {
|
|
17
|
+
throw new Error('缺少FEISHU_APP_ID或FEISHU_APP_SECRET环境变量');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.client = axios.create({
|
|
21
|
+
baseURL: this.baseURL,
|
|
22
|
+
timeout: 30000,
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// 请求拦截器:添加认证头
|
|
29
|
+
this.client.interceptors.request.use(async (config) => {
|
|
30
|
+
if (!this.accessToken) {
|
|
31
|
+
await this.refreshAccessToken();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
config.headers.Authorization = `Bearer ${this.accessToken}`;
|
|
35
|
+
return config;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 响应拦截器:处理token过期
|
|
39
|
+
this.client.interceptors.response.use(
|
|
40
|
+
(response) => response,
|
|
41
|
+
async (error) => {
|
|
42
|
+
const originalRequest = error.config;
|
|
43
|
+
|
|
44
|
+
// 如果是token过期错误且未重试过
|
|
45
|
+
if (error.response && error.response.status === 99991663 && !originalRequest._retry && this.autoRefreshToken) {
|
|
46
|
+
originalRequest._retry = true;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await this.refreshAccessToken();
|
|
50
|
+
originalRequest.headers.Authorization = `Bearer ${this.accessToken}`;
|
|
51
|
+
return this.client(originalRequest);
|
|
52
|
+
} catch (refreshError) {
|
|
53
|
+
return Promise.reject(refreshError);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Promise.reject(error);
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 刷新访问令牌
|
|
64
|
+
*/
|
|
65
|
+
async refreshAccessToken() {
|
|
66
|
+
try {
|
|
67
|
+
this.accessToken = await getAccessToken(this.appId, this.appSecret);
|
|
68
|
+
return this.accessToken;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(`刷新访问令牌失败: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 获取应用信息
|
|
76
|
+
*/
|
|
77
|
+
async getApp(appToken) {
|
|
78
|
+
validateRequired({ appToken }, ['appToken']);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.client.get(`/apps/${appToken}`);
|
|
82
|
+
return response.data;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw this.handleError(error, '获取应用信息');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 获取数据表列表
|
|
90
|
+
*/
|
|
91
|
+
async listTables(appToken, params = {}) {
|
|
92
|
+
validateRequired({ appToken }, ['appToken']);
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const response = await this.client.get(`/apps/${appToken}/tables`, { params });
|
|
96
|
+
return response.data;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
throw this.handleError(error, '获取数据表列表');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 创建数据表
|
|
104
|
+
*/
|
|
105
|
+
async createTable(appToken, tableData) {
|
|
106
|
+
validateRequired({ appToken }, ['appToken']);
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const response = await this.client.post(`/apps/${appToken}/tables`, tableData);
|
|
110
|
+
return response.data;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw this.handleError(error, '创建数据表');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 获取数据表详情
|
|
118
|
+
*/
|
|
119
|
+
async getTable(appToken, tableId) {
|
|
120
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}`);
|
|
124
|
+
return response.data;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw this.handleError(error, '获取数据表详情');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 删除数据表
|
|
132
|
+
*/
|
|
133
|
+
async deleteTable(appToken, tableId) {
|
|
134
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const response = await this.client.delete(`/apps/${appToken}/tables/${tableId}`);
|
|
138
|
+
return response.data;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw this.handleError(error, '删除数据表');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 获取字段列表
|
|
146
|
+
*/
|
|
147
|
+
async listFields(appToken, tableId, params = {}) {
|
|
148
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}/fields`, { params });
|
|
152
|
+
return response.data;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
throw this.handleError(error, '获取字段列表');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 创建字段
|
|
160
|
+
*/
|
|
161
|
+
async createField(appToken, tableId, fieldData) {
|
|
162
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const response = await this.client.post(`/apps/${appToken}/tables/${tableId}/fields`, fieldData);
|
|
166
|
+
return response.data;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw this.handleError(error, '创建字段');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 更新字段
|
|
174
|
+
*/
|
|
175
|
+
async updateField(appToken, tableId, fieldId, fieldData) {
|
|
176
|
+
validateRequired({ appToken, tableId, fieldId }, ['appToken', 'tableId', 'fieldId']);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const response = await this.client.put(`/apps/${appToken}/tables/${tableId}/fields/${fieldId}`, fieldData);
|
|
180
|
+
return response.data;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw this.handleError(error, '更新字段');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 获取记录列表
|
|
188
|
+
*/
|
|
189
|
+
async listRecords(appToken, tableId, params = {}) {
|
|
190
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}/records`, { params });
|
|
194
|
+
return response.data;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw this.handleError(error, '获取记录列表');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 创建记录
|
|
202
|
+
*/
|
|
203
|
+
async createRecord(appToken, tableId, recordData) {
|
|
204
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const response = await this.client.post(`/apps/${appToken}/tables/${tableId}/records`, {
|
|
208
|
+
fields: recordData
|
|
209
|
+
});
|
|
210
|
+
return response.data;
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw this.handleError(error, '创建记录');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 批量创建记录
|
|
218
|
+
*/
|
|
219
|
+
async batchCreateRecords(appToken, tableId, records) {
|
|
220
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const response = await this.client.post(`/apps/${appToken}/tables/${tableId}/records/batch_create`, {
|
|
224
|
+
records: records.map(record => ({ fields: record }))
|
|
225
|
+
});
|
|
226
|
+
return response.data;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
throw this.handleError(error, '批量创建记录');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 获取记录详情
|
|
234
|
+
*/
|
|
235
|
+
async getRecord(appToken, tableId, recordId) {
|
|
236
|
+
validateRequired({ appToken, tableId, recordId }, ['appToken', 'tableId', 'recordId']);
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}/records/${recordId}`);
|
|
240
|
+
return response.data;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
throw this.handleError(error, '获取记录详情');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 更新记录
|
|
248
|
+
*/
|
|
249
|
+
async updateRecord(appToken, tableId, recordId, recordData) {
|
|
250
|
+
validateRequired({ appToken, tableId, recordId }, ['appToken', 'tableId', 'recordId']);
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const response = await this.client.put(`/apps/${appToken}/tables/${tableId}/records/${recordId}`, {
|
|
254
|
+
fields: recordData
|
|
255
|
+
});
|
|
256
|
+
return response.data;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
throw this.handleError(error, '更新记录');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 批量更新记录
|
|
264
|
+
*/
|
|
265
|
+
async batchUpdateRecords(appToken, tableId, records) {
|
|
266
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const response = await this.client.post(`/apps/${appToken}/tables/${tableId}/records/batch_update`, {
|
|
270
|
+
records: records.map(record => ({
|
|
271
|
+
record_id: record.record_id,
|
|
272
|
+
fields: record.fields
|
|
273
|
+
}))
|
|
274
|
+
});
|
|
275
|
+
return response.data;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw this.handleError(error, '批量更新记录');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 删除记录
|
|
283
|
+
*/
|
|
284
|
+
async deleteRecord(appToken, tableId, recordId) {
|
|
285
|
+
validateRequired({ appToken, tableId, recordId }, ['appToken', 'tableId', 'recordId']);
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
const response = await this.client.delete(`/apps/${appToken}/tables/${tableId}/records/${recordId}`);
|
|
289
|
+
return response.data;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
throw this.handleError(error, '删除记录');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 批量删除记录
|
|
297
|
+
*/
|
|
298
|
+
async batchDeleteRecords(appToken, tableId, recordIds) {
|
|
299
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const response = await this.client.post(`/apps/${appToken}/tables/${tableId}/records/batch_delete`, {
|
|
303
|
+
records: recordIds.map(recordId => ({ record_id: recordId }))
|
|
304
|
+
});
|
|
305
|
+
return response.data;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
throw this.handleError(error, '批量删除记录');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 获取视图列表
|
|
313
|
+
*/
|
|
314
|
+
async listViews(appToken, tableId, params = {}) {
|
|
315
|
+
validateRequired({ appToken, tableId }, ['appToken', 'tableId']);
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}/views`, { params });
|
|
319
|
+
return response.data;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
throw this.handleError(error, '获取视图列表');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 获取视图详情
|
|
327
|
+
*/
|
|
328
|
+
async getView(appToken, tableId, viewId) {
|
|
329
|
+
validateRequired({ appToken, tableId, viewId }, ['appToken', 'tableId', 'viewId']);
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
const response = await this.client.get(`/apps/${appToken}/tables/${tableId}/views/${viewId}`);
|
|
333
|
+
return response.data;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw this.handleError(error, '获取视图详情');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 错误处理
|
|
341
|
+
*/
|
|
342
|
+
handleError(error, operation) {
|
|
343
|
+
if (error.response) {
|
|
344
|
+
const { status, data } = error.response;
|
|
345
|
+
const errorMsg = data.msg || data.message || error.message;
|
|
346
|
+
|
|
347
|
+
const fullError = new Error(`${operation}失败: ${errorMsg} (状态码: ${status})`);
|
|
348
|
+
fullError.status = status;
|
|
349
|
+
fullError.data = data;
|
|
350
|
+
|
|
351
|
+
return fullError;
|
|
352
|
+
} else if (error.request) {
|
|
353
|
+
return new Error(`${operation}失败: 网络错误,无法连接到飞书服务器`);
|
|
354
|
+
} else {
|
|
355
|
+
return new Error(`${operation}失败: ${error.message}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* 测试连接
|
|
361
|
+
*/
|
|
362
|
+
async testConnection() {
|
|
363
|
+
try {
|
|
364
|
+
await this.refreshAccessToken();
|
|
365
|
+
return {
|
|
366
|
+
success: true,
|
|
367
|
+
message: '连接测试成功',
|
|
368
|
+
appId: this.appId,
|
|
369
|
+
tokenValid: !!this.accessToken
|
|
370
|
+
};
|
|
371
|
+
} catch (error) {
|
|
372
|
+
return {
|
|
373
|
+
success: false,
|
|
374
|
+
message: `连接测试失败: ${error.message}`,
|
|
375
|
+
appId: this.appId
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
module.exports = FeishuBitableAPI;
|