技能备份 - 2026-04-15 (40个技能)

This commit is contained in:
root
2026-04-15 18:53:15 +08:00
parent f62f14814f
commit c65fce24e4
791 changed files with 190773 additions and 0 deletions
+486
View File
@@ -0,0 +1,486 @@
#!/usr/bin/env python3
"""
Feishu Smart Doc Writer
飞书智能文档写入器 - 自动分段、分批写入
支持首次使用自动引导配置
核心功能:
1. 智能分块写入 - 解决飞书API字数限制导致的空白文档
2. 自动转移所有权 - 创建文档后自动转移给用户
3. 首次使用引导 - 自动询问OpenID并配置
"""
import json
import os
from typing import Dict, Optional
from dataclasses import dataclass, asdict
# 配置文件路径
CONFIG_PATH = os.path.expanduser("~/.openclaw/workspace/skills/feishu-smart-doc-writer/user_config.json")
@dataclass
class UserConfig:
"""用户配置"""
owner_openid: str = ""
permission_noted: bool = False # 用户是否已确认权限
first_time: bool = True # 是否首次使用
def save(self):
"""保存配置"""
try:
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
with open(CONFIG_PATH, 'w', encoding='utf-8') as f:
json.dump(asdict(self), f, ensure_ascii=False, indent=2)
return True
except Exception as e:
print(f"保存配置失败: {e}")
return False
@classmethod
def load(cls) -> 'UserConfig':
"""加载配置"""
try:
if os.path.exists(CONFIG_PATH):
with open(CONFIG_PATH, 'r', encoding='utf-8') as f:
data = json.load(f)
return cls(**data)
except Exception as e:
print(f"加载配置失败: {e}")
return cls()
# 引导消息模板
FIRST_TIME_GUIDE = """👋 **欢迎使用 Feishu Smart Doc Writer**
本 Skill 可以帮助你:
✅ **智能分块写入** - 解决长文档写入时因API限制导致的空白问题
✅ **自动转移所有权** - 创建文档后自动转移给你,拥有完全控制权
---
## 🔧 首次使用配置
### 第1步:获取你的 OpenID
**详细步骤(精确路径):**
1. **登录飞书开放平台**
- 网址:https://open.feishu.cn
2. **进入权限管理并前往调试台**
- 进入你的**相关应用**
- 点击 **"权限管理"**
- 搜索权限:`im:message`
- 鼠标移动到 **"相关API事件"**
- 选择:**【API】发送消息**
- 点击右下角:**"前往API调试台"**
3. **找到 "快速复制 open_id"**
- 在页面中找到 **蓝色文字** "快速复制 open_id"
- 点击这个链接
4. **选择用户并复制**
- 在弹出的选择框中,**选择你的账号**
- 点击 **"复制"** 按钮
- 得到格式如:`ou_5b921cba0fd6e7c885276a02d730ec19`
💡 **提示**OpenID 是以 `ou_` 开头的一串字符,不是数字ID
---
### 第2步:开通并发布权限
⚠️ **重要:需要开通权限并发布应用新版本**
**开通权限步骤:**
1. **进入权限管理**
- 登录 https://open.feishu.cn
- 进入你的应用
- 点击左侧菜单 **"权限管理"**
2. **搜索并开通权限**
- 在搜索框输入:`docs:permission.member:transfer`
- 找到权限 **"转移云文档的所有权"**
- 点击 **"开通"** 按钮
3. **发布新版本(关键!)**
- 开通后,点击页面右上角的 **"发布"** 按钮
- 等待发布完成(显示"已发布"状态)
- ⚠️ **不发布的话,权限不会生效!**
---
## 💬 请回复配置信息
请按以下格式回复:
```
配置OpenIDou_你的OpenID
权限已开通并发布:是
```
例如:
```
配置OpenIDou_5b921cba0fd6e7c885276a02d730ec19
权限已开通并发布:是
```
配置完成后,本 Skill 将自动保存配置,之后创建文档会自动转移所有权给你!
"""
async def write_smart(ctx, args: dict) -> dict:
"""
智能创建飞书文档(自动分块 + 自动转移所有权)
首次使用时会自动引导配置。
Args:
ctx: OpenClaw 上下文
args: 包含 title, content, folder_token, chunk_size, show_progress
Returns:
{"doc_url": "...", "doc_token": "...", "chunks_count": N, "owner_transferred": True/False}
"""
# 加载用户配置
config = UserConfig.load()
# 首次使用或未完成配置,显示引导
if config.first_time or not config.owner_openid:
return {
"doc_url": None,
"doc_token": None,
"chunks_count": 0,
"owner_transferred": False,
"need_config": True,
"message": FIRST_TIME_GUIDE
}
# 已配置,正常执行
title = args.get("title")
content = args.get("content", "")
folder_token = args.get("folder_token")
chunk_size = args.get("chunk_size", 2000)
show_progress = args.get("show_progress", True)
if not title:
raise ValueError("必须提供 title 参数")
from .feishu_smart_doc_writer import FeishuDocWriter, ChunkConfig
chunk_config = ChunkConfig(
chunk_size=chunk_size,
show_progress=show_progress
)
writer = FeishuDocWriter(ctx, chunk_config)
try:
# 使用配置的 owner_openid 自动转移
result = await writer.write_document_with_transfer(
title=title,
content=content,
folder_token=folder_token,
owner_openid=config.owner_openid
)
transfer_msg = ",所有权已转移" if result.get("owner_transferred") else ""
return {
"doc_url": result["doc_url"],
"doc_token": result["doc_token"],
"chunks_count": result["chunks_count"],
"owner_transferred": result["owner_transferred"],
"need_config": False,
"message": f"✅ 文档创建成功,共分 {result['chunks_count']} 块写入{transfer_msg}"
}
except Exception as e:
return {
"doc_url": None,
"doc_token": None,
"chunks_count": 0,
"owner_transferred": False,
"need_config": False,
"message": f"❌ 创建失败: {e}"
}
async def configure(ctx, args: dict) -> dict:
"""
配置 Skill
用户首次使用时,通过此工具配置 OpenID。
Args:
ctx: OpenClaw 上下文
args: 包含 openid, permission_checked
Returns:
{"success": True/False, "message": "..."}
"""
openid = args.get("openid", "").strip()
permission_checked = args.get("permission_checked", False)
# 验证 OpenID 格式
if not openid:
return {
"success": False,
"message": "❌ 请提供 OpenID"
}
if not openid.startswith("ou_"):
return {
"success": False,
"message": "❌ OpenID 格式错误,应以 'ou_' 开头,请检查"
}
# 保存配置
config = UserConfig()
config.owner_openid = openid
config.permission_noted = permission_checked
config.first_time = False
if config.save():
return {
"success": True,
"openid": openid,
"message": f"✅ 配置成功!\n\n你的 OpenID{openid}\n\n配置已保存,现在可以使用 write_smart 创建文档,所有权会自动转移给你。"
}
else:
return {
"success": False,
"message": "❌ 配置保存失败"
}
async def append_smart(ctx, args: dict) -> dict:
"""
智能追加内容到飞书文档(自动分块)
Args:
ctx: OpenClaw 上下文
args: 包含 doc_url, content, chunk_size, show_progress
Returns:
{"success": True/False, "chunks_count": N}
"""
doc_url = args.get("doc_url")
content = args.get("content", "")
chunk_size = args.get("chunk_size", 2000)
show_progress = args.get("show_progress", True)
if not doc_url:
raise ValueError("必须提供 doc_url 参数")
from .feishu_smart_doc_writer import FeishuDocWriter, ChunkConfig, ContentChunker
config = ChunkConfig(
chunk_size=chunk_size,
show_progress=show_progress
)
writer = FeishuDocWriter(ctx, config)
try:
success = await writer.append_to_document(doc_url, content)
# 计算分块数
chunks = ContentChunker(config).chunk_content(content)
return {
"success": success,
"chunks_count": len(chunks),
"message": f"{'' if success else ''} 追加 {'成功' if success else '失败'},共分 {len(chunks)}"
}
except Exception as e:
return {
"success": False,
"chunks_count": 0,
"message": f"❌ 追加失败: {e}"
}
async def transfer_ownership(ctx, args: dict) -> dict:
"""
转移文档所有权
Args:
ctx: OpenClaw 上下文
args: 包含 doc_url, owner_openid
Returns:
{"success": True/False, "message": "..."}
"""
doc_url = args.get("doc_url")
owner_openid = args.get("owner_openid")
if not doc_url or not owner_openid:
raise ValueError("必须提供 doc_url 和 owner_openid 参数")
from .feishu_smart_doc_writer import FeishuDocWriter, ChunkConfig
config = ChunkConfig(show_progress=False)
writer = FeishuDocWriter(ctx, config)
try:
success = await writer.transfer_ownership(doc_url, owner_openid)
return {
"success": success,
"message": f"{'' if success else ''} 所有权转移{'成功' if success else '失败'}"
}
except Exception as e:
return {
"success": False,
"message": f"❌ 转移失败: {e}"
}
async def get_config_status(ctx, args: dict) -> dict:
"""
获取当前配置状态
Returns:
{"configured": True/False, "openid": "...", "message": "..."}
"""
config = UserConfig.load()
if config.owner_openid:
return {
"configured": True,
"openid": config.owner_openid,
"first_time": config.first_time,
"message": f"✅ 已配置\nOpenID: {config.owner_openid}"
}
else:
return {
"configured": False,
"openid": None,
"first_time": config.first_time,
"message": "⚠️ 未配置\n请使用 configure 工具进行配置"
}
async def search_docs(ctx, args: dict) -> dict:
"""
搜索本地索引中的文档
Args:
ctx: OpenClaw 上下文
args: 包含 keyword, search_in(可选)
Returns:
{"results": [...], "count": N, "message": "..."}
"""
keyword = args.get("keyword", "").strip()
search_in = args.get("search_in", ["name", "summary", "tags"])
if not keyword:
return {
"results": [],
"count": 0,
"message": "❌ 请提供搜索关键词"
}
try:
from .index_manager import IndexManager
manager = IndexManager()
results = manager.search_docs(keyword, search_in)
# 格式化结果
formatted_results = []
for doc in results:
formatted_results.append({
"name": doc.get("name", ""),
"type": doc.get("type", ""),
"link": doc.get("link", ""),
"summary": doc.get("summary", ""),
"status": doc.get("status", ""),
"tags": doc.get("tags", ""),
"updated": doc.get("updated", "")
})
return {
"results": formatted_results,
"count": len(formatted_results),
"message": f"✅ 找到 {len(formatted_results)} 个结果" if formatted_results else f"⚠️ 未找到包含 '{keyword}' 的文档"
}
except Exception as e:
return {
"results": [],
"count": 0,
"message": f"❌ 搜索失败: {e}"
}
async def list_docs(ctx, args: dict) -> dict:
"""
列出所有文档(支持筛选)
Args:
ctx: OpenClaw 上下文
args: 包含 tag(可选), status(可选), limit(可选)
Returns:
{"results": [...], "count": N, "message": "..."}
"""
tag = args.get("tag")
status = args.get("status")
limit = args.get("limit", 50)
try:
from .index_manager import IndexManager
manager = IndexManager()
results = manager.list_docs(tag=tag, status=status, limit=limit)
# 格式化结果
formatted_results = []
for doc in results:
formatted_results.append({
"name": doc.get("name", ""),
"type": doc.get("type", ""),
"link": doc.get("link", ""),
"summary": doc.get("summary", ""),
"status": doc.get("status", ""),
"tags": doc.get("tags", ""),
"updated": doc.get("updated", "")
})
# 构建消息
filter_desc = []
if tag:
filter_desc.append(f"标签 '{tag}'")
if status:
filter_desc.append(f"状态 '{status}'")
filter_text = "".join(filter_desc) if filter_desc else "全部"
return {
"results": formatted_results,
"count": len(formatted_results),
"message": f"{filter_text}文档共 {len(formatted_results)}"
}
except Exception as e:
return {
"results": [],
"count": 0,
"message": f"❌ 列出文档失败: {e}"
}
# 版本信息
__version__ = "1.3.0"
__all__ = [
"write_smart",
"append_smart",
"transfer_ownership",
"configure",
"get_config_status",
"search_docs",
"list_docs",
"UserConfig",
"FIRST_TIME_GUIDE"
]