feat(slides): support importing pptx as slides (#1068)

This commit is contained in:
ethan-zhx
2026-05-26 11:54:46 +08:00
committed by GitHub
parent fe72e41fb2
commit ee9d090e64
6 changed files with 78 additions and 13 deletions

View File

@@ -19,7 +19,7 @@ import (
var DriveImport = common.Shortcut{
Service: "drive",
Command: "+import",
Description: "Import a local file to Drive as a cloud document (docx, sheet, bitable)",
Description: "Import a local file to Drive as a cloud document (docx, sheet, bitable, slides)",
Risk: "write",
Scopes: []string{
"docs:document.media:upload",
@@ -27,8 +27,8 @@ var DriveImport = common.Shortcut{
},
AuthTypes: []string{"user", "bot"},
Flags: []common.Flag{
{Name: "file", Desc: "local file path (e.g. .docx, .xlsx, .md, .base; large files auto use multipart upload; .base is capped at 20MB)", Required: true},
{Name: "type", Desc: "target document type (docx, sheet, bitable)", Required: true},
{Name: "file", Desc: "local file path (e.g. .docx, .xlsx, .md, .base, .pptx; large files auto use multipart upload; .base is capped at 20MB, .pptx at 500MB)", Required: true},
{Name: "type", Desc: "target document type (docx, sheet, bitable, slides)", Required: true},
{Name: "folder-token", Desc: "target folder token (omit for root folder; API accepts empty mount_key as root)"},
{Name: "name", Desc: "imported file name (default: local file name without extension)"},
{Name: "target-token", Desc: "existing token to import data into (only for type=bitable); when set, data is mounted into this bitable instead of creating a new one"},
@@ -221,10 +221,10 @@ func appendDriveImportUploadDryRun(dry *common.DryRunAPI, spec driveImportSpec,
// from the API and isn't normalized; if it ever returns aliases like "sheets"
// or "sheet_v2" the URL construction would silently fall through. Fall back
// to the user-supplied --type, which is already validated to docx/sheet/
// bitable, so out.url stays populated whenever status.Token is set.
// bitable/slides, so out.url stays populated whenever status.Token is set.
func normalizeDriveImportKindForURL(serverType, fallback string) string {
switch strings.ToLower(strings.TrimSpace(serverType)) {
case "docx", "sheet", "bitable":
case "docx", "sheet", "bitable", "slides":
return strings.ToLower(strings.TrimSpace(serverType))
}
return fallback

View File

@@ -25,6 +25,7 @@ const (
// These limits follow the current product-side import constraints per format.
driveImport20MBFileSizeLimit int64 = 20 * 1024 * 1024
driveImport100MBFileSizeLimit int64 = 100 * 1024 * 1024
driveImport500MBFileSizeLimit int64 = 500 * 1024 * 1024
driveImport600MBFileSizeLimit int64 = 600 * 1024 * 1024
driveImport800MBFileSizeLimit int64 = 800 * 1024 * 1024
)
@@ -43,6 +44,7 @@ var driveImportExtToDocTypes = map[string][]string{
"xls": {"sheet"},
"csv": {"sheet", "bitable"},
"base": {"bitable"},
"pptx": {"slides"},
}
// driveImportSpec contains the user-facing import inputs after normalization.
@@ -151,6 +153,8 @@ func driveImportFileSizeLimit(filePath, docType string) (int64, bool) {
switch strings.TrimPrefix(strings.ToLower(filepath.Ext(filePath)), ".") {
case "docx", "doc":
return driveImport600MBFileSizeLimit, true
case "pptx":
return driveImport500MBFileSizeLimit, true
case "txt", "md", "mark", "markdown", "html", "xls", "base":
return driveImport20MBFileSizeLimit, true
case "xlsx":
@@ -195,18 +199,18 @@ func validateDriveImportFileSize(filePath, docType string, fileSize int64) error
func validateDriveImportSpec(spec driveImportSpec) error {
ext := spec.FileExtension()
if ext == "" {
return output.ErrValidation("file must have an extension (e.g. .md, .docx, .xlsx)")
return output.ErrValidation("file must have an extension (e.g. .md, .docx, .xlsx, .pptx)")
}
switch spec.DocType {
case "docx", "sheet", "bitable":
case "docx", "sheet", "bitable", "slides":
default:
return output.ErrValidation("unsupported target document type: %s. Supported types are: docx, sheet, bitable", spec.DocType)
return output.ErrValidation("unsupported target document type: %s. Supported types are: docx, sheet, bitable, slides", spec.DocType)
}
supportedTypes, ok := driveImportExtToDocTypes[ext]
if !ok {
return output.ErrValidation("unsupported file extension: %s. Supported extensions are: docx, doc, txt, md, mark, markdown, html, xlsx, xls, csv, base", ext)
return output.ErrValidation("unsupported file extension: %s. Supported extensions are: docx, doc, txt, md, mark, markdown, html, xlsx, xls, csv, base, pptx", ext)
}
typeAllowed := false
@@ -227,6 +231,8 @@ func validateDriveImportSpec(spec driveImportSpec) error {
hint = fmt.Sprintf(".xls files can only be imported as 'sheet', not '%s'", spec.DocType)
case "base":
hint = fmt.Sprintf(".base files can only be imported as 'bitable', not '%s'", spec.DocType)
case "pptx":
hint = fmt.Sprintf(".pptx files can only be imported as 'slides', not '%s'", spec.DocType)
default:
hint = fmt.Sprintf(".%s files can only be imported as 'docx', not '%s'", ext, spec.DocType)
}

View File

@@ -35,11 +35,20 @@ func TestValidateDriveImportSpec(t *testing.T) {
name: "base bitable ok",
spec: driveImportSpec{FilePath: "./snapshot.base", DocType: "bitable"},
},
{
name: "pptx slides ok",
spec: driveImportSpec{FilePath: "./deck.pptx", DocType: "slides"},
},
{
name: "base non bitable rejected",
spec: driveImportSpec{FilePath: "./snapshot.base", DocType: "sheet"},
wantErr: ".base files can only be imported as 'bitable'",
},
{
name: "pptx non slides rejected",
spec: driveImportSpec{FilePath: "./deck.pptx", DocType: "docx"},
wantErr: ".pptx files can only be imported as 'slides'",
},
{
name: "unknown extension rejected",
spec: driveImportSpec{FilePath: "./data.rtf", DocType: "docx"},
@@ -114,6 +123,19 @@ func TestValidateDriveImportFileSize(t *testing.T) {
docType: "sheet",
fileSize: driveImport800MBFileSizeLimit,
},
{
name: "pptx exceeds 500mb limit",
filePath: "./deck.pptx",
docType: "slides",
fileSize: driveImport500MBFileSizeLimit + 1,
wantText: "exceeds 500.0 MB import limit for .pptx",
},
{
name: "pptx within 500mb limit",
filePath: "./deck.pptx",
docType: "slides",
fileSize: driveImport500MBFileSizeLimit,
},
{
name: "base exceeds 20mb limit",
filePath: "./snapshot.base",

View File

@@ -732,3 +732,33 @@ func TestDriveImportFallbackURLWhenServerTypeIsAlias(t *testing.T) {
t.Fatalf("data.url = %#v, want %q (alias normalized via spec.DocType fallback)", got, want)
}
}
func TestDriveImportFallbackURLForSlides(t *testing.T) {
f, stdout, _, reg := cmdutil.TestFactory(t, driveImportTestConfig("slides"))
driveImportMockEnv(t, reg, "ticket_slides", map[string]interface{}{
"token": "sldcn_imported",
"type": "slides",
"job_status": float64(0),
})
tmpDir := t.TempDir()
origDir, _ := os.Getwd()
if err := os.Chdir(tmpDir); err != nil {
t.Fatalf("Chdir: %v", err)
}
defer os.Chdir(origDir)
if err := os.WriteFile("deck.pptx", []byte("fake-pptx"), 0o644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
if err := mountAndRunDrive(t, DriveImport, []string{
"+import", "--file", "deck.pptx", "--type", "slides", "--as", "user",
}, f, stdout); err != nil {
t.Fatalf("import should succeed, got: %v", err)
}
data := decodeDriveEnvelope(t, stdout)
if got, want := data["url"], "https://www.feishu.cn/slides/sldcn_imported"; got != want {
t.Fatalf("data.url = %#v, want %q (slides fallback)", got, want)
}
}

View File

@@ -1,7 +1,7 @@
---
name: lark-drive
version: 1.0.0
description: "飞书云空间(云盘/云存储):管理云空间(云盘/云存储)中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题docx、sheet、bitable、file、folder、wiki也负责把本地 Word/Markdown/Excel/CSV 以及 Base 快照(.base导入为飞书在线云文档docx、sheet、bitable。当用户需要上传或下载文件、整理云空间云盘/云存储)目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base 时使用。\"云空间\"、\"云盘\"和\"云存储\"是同一概念,用户说\"云盘\"、\"云存储\"、\"网盘\"、\"我的空间\"时均路由到本 skill。"
description: "飞书云空间(云盘/云存储):管理云空间(云盘/云存储)中的文件和文件夹。上传和下载文件、创建文件夹、复制/移动/删除文件、查看文件元数据、管理文档评论、管理文档权限、订阅用户评论变更事件、修改文件标题docx、sheet、bitable、file、folder、wiki也负责把本地 Word/Markdown/Excel/CSV/PPTX 以及 Base 快照(.base导入为飞书在线云文档docx、sheet、bitable、slides)。当用户需要上传或下载文件、整理云空间(云盘/云存储)目录、查看文件详情、管理评论、管理文档权限、修改文件标题、订阅用户评论变更事件,或要把本地文件导入成新版文档、电子表格、多维表格/Base/幻灯片 时使用。\"云空间\"、\"云盘\"和\"云存储\"是同一概念,用户说\"云盘\"、\"云存储\"、\"网盘\"、\"我的空间\"时均路由到本 skill。"
metadata:
requires:
bins: ["lark-cli"]
@@ -21,6 +21,7 @@ metadata:
- 用户要**搜文档 / Wiki / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--mine`,实为 owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag避免手写嵌套 JSON。
- 用户要把本地 `.xlsx` / `.csv` / `.base` 导入成 Base / 多维表格 / bitable第一步必须使用 `lark-cli drive +import --type bitable`
- 用户要把本地 `.md` / `.docx` / `.doc` / `.txt` / `.html` 导入成在线文档,使用 `lark-cli drive +import --type docx`
- 用户要把本地 `.pptx` 导入成飞书幻灯片,使用 `lark-cli drive +import --type slides`;当前 PPTX 导入上限是 500MB。
- 用户要在 Drive 里上传、创建、读取、局部 patch 或覆盖更新**原生 `.md` 文件**(不是导入成 docx切到 [`lark-markdown`](../lark-markdown/SKILL.md)。
- 用户要比较原生 `.md` 文件的**历史版本差异**,或比较远端 Markdown 与本地草稿,切到 [`lark-markdown`](../lark-markdown/SKILL.md) 的 `lark-cli markdown +diff`;需要版本号时先用 `drive +version-history`
- 用户要查看、下载、回滚或删除文件的**历史版本**,使用 `drive +version-history``drive +version-get``drive +version-revert``drive +version-delete`;这组命令同时支持 `--as user``--as bot`,自动化场景优先 `--as bot`
@@ -271,7 +272,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli drive +<verb> [flags]`
| [`+add-comment`](references/lark-drive-add-comment.md) | Add a comment to doc/docx/file/sheet/slides, also supports wiki URL resolving to doc/docx/file/sheet/slides; file targets support selected extensions and full comments only |
| [`+export`](references/lark-drive-export.md) | Export a doc/docx/sheet/bitable/slides to a local file with limited polling; supports `--file-name` for local naming |
| [`+export-download`](references/lark-drive-export-download.md) | Download an exported file by file_token |
| [`+import`](references/lark-drive-import.md) | Import a local file to Drive as a cloud document (docx, sheet, bitable) |
| [`+import`](references/lark-drive-import.md) | Import a local file to Drive as a cloud document (docx, sheet, bitable, slides) |
| [`+version-history`](references/lark-drive-version-history.md) | List historical versions of a file with only_tag=true and cursor-based pagination |
| [`+version-get`](references/lark-drive-version-get.md) | Download a specific historical version of a file |
| [`+version-revert`](references/lark-drive-version-revert.md) | Revert a file to a specific historical version |

View File

@@ -2,7 +2,7 @@
> **前置条件:** 先阅读 [`../lark-shared/SKILL.md`](../../lark-shared/SKILL.md) 了解认证、全局参数和安全规则。
将本地文件(如 Word、TXT、Markdown、Excel 等导入并转换为飞书在线云文档docx、sheet、bitable。底层统一通过 `POST /open-apis/drive/v1/import_tasks` 接口创建导入任务,并在 shortcut 内做有限次数轮询 `GET /open-apis/drive/v1/import_tasks/:ticket`
将本地文件(如 Word、TXT、Markdown、Excel、PPTX导入并转换为飞书在线云文档docx、sheet、bitable、slides)。底层统一通过 `POST /open-apis/drive/v1/import_tasks` 接口创建导入任务,并在 shortcut 内做有限次数轮询 `GET /open-apis/drive/v1/import_tasks/:ticket`
> [!IMPORTANT]
> 当用户说“把本地 Excel / CSV / `.base` 快照导入成 Base / 多维表格 / bitable 文档”时,第一步必须使用 `drive +import --type bitable`。
@@ -40,6 +40,9 @@ lark-cli drive +import --file ./crm.xlsx --type bitable --name "客户台账"
# 导入 .base 快照为多维表格 / Base (bitable)(文件不能超过 20MB
lark-cli drive +import --file ./snapshot.base --type bitable --name "快照还原"
# 导入 PPTX 为飞书幻灯片 (slides)(文件不能超过 500MB
lark-cli drive +import --file ./deck.pptx --type slides --name "项目汇报"
# 导入到指定文件夹,并指定导入后的文件名
lark-cli drive +import --file ./data.csv --type bitable --folder-token <FOLDER_TOKEN> --name "导入数据表"
@@ -55,7 +58,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run
| 参数 | 必填 | 说明 |
|------|------|------|
| `--file` | 是 | 本地文件路径,根据文件后缀名自动推断 `file_extension`;文件需满足对应格式的导入大小限制,超过 20MB 且仍在允许范围内时会自动切换分片上传 |
| `--type` | 是 | 导入目标云文档格式。可选值:`docx` (新版文档)、`sheet` (电子表格)、`bitable` (多维表格) |
| `--type` | 是 | 导入目标云文档格式。可选值:`docx` (新版文档)、`sheet` (电子表格)、`bitable` (多维表格)`slides` (飞书幻灯片) |
| `--folder-token` | 否 | 目标文件夹 token不传则请求中的 `point.mount_key` 为空字符串Import API 会将其解释为导入到云空间(云盘/云存储)根目录 |
| `--name` | 否 | 导入后的在线云文档名称,不传默认使用本地文件名去掉扩展名后的结果 |
| `--target-token` | 否 | 已有的多维表格 token将数据导入到该多维表格中**仅支持 `--type bitable`**);传入后数据会挂载到目标多维表格而非新建一个 |
@@ -85,6 +88,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run
| `.xls` | `sheet` | Microsoft Excel 97-2003 表格 |
| `.csv` | `sheet`, `bitable` | CSV 数据文件 |
| `.base` | `bitable` | 多维表格快照文件 |
| `.pptx` | `slides` | Microsoft PowerPoint 演示文稿 |
> [!IMPORTANT]
> 用户口头说的 “Base” / “多维表格” / “bitable”在命令里统一对应 `--type bitable`。
@@ -94,6 +98,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run
> - `.xlsx` / `.csv` 文件**只能**导入为 `sheet` 或 `bitable`
> - `.xls` 文件**只能**导入为 `sheet`
> - `.base` 文件**只能**导入为 `bitable`
> - `.pptx` 文件**只能**导入为 `slides`
> - 例如:`.csv` 文件不能导入为 `docx``.md` 文件不能导入为 `sheet`
> [!IMPORTANT]
@@ -127,6 +132,7 @@ lark-cli drive +import --file ./README.md --type docx --dry-run
| `.csv` | `bitable` | 100MB |
| `.xls` | `sheet` | 20MB |
| `.base` | `bitable` | 20MB |
| `.pptx` | `slides` | 500MB |
- 如果文件超出对应上限shortcut 会在真正上传前直接返回验证错误。
- “超过 20MB 自动切换分片上传”只表示上传链路会切到 multipart不代表所有格式都允许导入超过 20MB 的文件。