技能备份 - 2026-04-15 (40个技能)
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
"""
|
||||
Feishu 文档索引管理器
|
||||
负责管理 memory/feishu-docs-index.md 文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
|
||||
|
||||
class IndexManager:
|
||||
"""飞书文档索引管理器"""
|
||||
|
||||
DEFAULT_INDEX_PATH = os.path.expanduser("~/.openclaw/workspace/memory/feishu-docs-index.md")
|
||||
|
||||
def __init__(self, index_path: str = None):
|
||||
self.index_path = index_path or self.DEFAULT_INDEX_PATH
|
||||
self._ensure_index_exists()
|
||||
|
||||
def _ensure_index_exists(self):
|
||||
"""确保索引文件存在"""
|
||||
if not os.path.exists(self.index_path):
|
||||
os.makedirs(os.path.dirname(self.index_path), exist_ok=True)
|
||||
self._create_empty_index()
|
||||
|
||||
def _create_empty_index(self):
|
||||
"""创建空索引文件"""
|
||||
content = """# 飞书云文档索引
|
||||
|
||||
**用途:** 快速定位和管理所有飞书云文档
|
||||
|
||||
---
|
||||
|
||||
## 📊 文档列表
|
||||
|
||||
| 序号 | 文档名 | 类型 | 链接 | 内容摘要 | 状态 | 最后更新 | 标签 | 所有者 |
|
||||
|------|--------|------|------|----------|------|----------|------|--------|
|
||||
|
||||
---
|
||||
|
||||
## 📂 按类型分类
|
||||
|
||||
### 项目管理
|
||||
|
||||
### 技术文档
|
||||
|
||||
### 每日归档
|
||||
|
||||
### 更新记录
|
||||
|
||||
### 数据分析
|
||||
|
||||
---
|
||||
|
||||
## 🔍 快速查找
|
||||
|
||||
*暂无关键词索引*
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用说明
|
||||
|
||||
**添加新文档时:**
|
||||
1. 复制表格中的一行
|
||||
2. 填写所有字段
|
||||
3. 更新分类索引
|
||||
|
||||
**查找文档时:**
|
||||
1. 先搜索本文档中的关键词
|
||||
2. 找到对应链接
|
||||
3. 用 `feishu_doc` 工具读取内容
|
||||
|
||||
---
|
||||
*创建时间:{date}*
|
||||
*最后更新:{date}*
|
||||
""".format(date=datetime.now().strftime("%Y-%m-%d"))
|
||||
|
||||
with open(self.index_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
def add_or_update_doc(self, name: str, doc_type: str, url: str, token: str,
|
||||
summary: str = "", status: str = "已完成",
|
||||
tags: List[str] = None, owner: str = "") -> bool:
|
||||
"""
|
||||
添加或更新文档到索引
|
||||
|
||||
Args:
|
||||
name: 文档名称
|
||||
doc_type: 文档类型 (docx, sheet, bitable 等)
|
||||
url: 文档链接
|
||||
token: 文档token
|
||||
summary: 内容摘要
|
||||
status: 文档状态
|
||||
tags: 标签列表
|
||||
owner: 所有者
|
||||
|
||||
Returns:
|
||||
是否成功
|
||||
"""
|
||||
try:
|
||||
# 读取现有索引
|
||||
with open(self.index_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 检查文档是否已存在(通过token判断)
|
||||
existing_index = self._find_doc_index(content, token)
|
||||
|
||||
# 获取当前序号
|
||||
if existing_index:
|
||||
doc_index = existing_index
|
||||
else:
|
||||
doc_index = self._get_next_index(content)
|
||||
|
||||
# 准备标签字符串
|
||||
tags_str = ", ".join(tags) if tags else ""
|
||||
|
||||
# 准备表格行
|
||||
now = datetime.now().strftime("%Y-%m-%d")
|
||||
new_row = f"| {doc_index} | {name} | {doc_type} | {url} | {summary} | {status} | {now} | {tags_str} | {owner} |"
|
||||
|
||||
# 更新表格
|
||||
if existing_index:
|
||||
# 更新现有行
|
||||
content = self._replace_table_row(content, token, new_row)
|
||||
else:
|
||||
# 添加新行
|
||||
content = self._insert_table_row(content, new_row)
|
||||
|
||||
# 更新分类(如果新文档)
|
||||
if not existing_index and tags:
|
||||
content = self._update_categories(content, name, tags)
|
||||
|
||||
# 更新关键词索引
|
||||
if not existing_index:
|
||||
content = self._update_keywords(content, name, summary, tags)
|
||||
|
||||
# 更新最后更新时间
|
||||
content = self._update_last_modified(content)
|
||||
|
||||
# 写回文件
|
||||
with open(self.index_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"更新索引失败: {e}")
|
||||
return False
|
||||
|
||||
def search_docs(self, keyword: str, search_in: List[str] = None) -> List[Dict]:
|
||||
"""
|
||||
搜索文档
|
||||
|
||||
Args:
|
||||
keyword: 搜索关键词
|
||||
search_in: 在哪些字段中搜索 (name, summary, tags)
|
||||
|
||||
Returns:
|
||||
匹配的文档列表
|
||||
"""
|
||||
if search_in is None:
|
||||
search_in = ["name", "summary", "tags"]
|
||||
|
||||
results = []
|
||||
docs = self._parse_table()
|
||||
|
||||
keyword_lower = keyword.lower()
|
||||
|
||||
for doc in docs:
|
||||
match = False
|
||||
if "name" in search_in and keyword_lower in doc.get("name", "").lower():
|
||||
match = True
|
||||
if "summary" in search_in and keyword_lower in doc.get("summary", "").lower():
|
||||
match = True
|
||||
if "tags" in search_in and keyword_lower in doc.get("tags", "").lower():
|
||||
match = True
|
||||
|
||||
if match:
|
||||
results.append(doc)
|
||||
|
||||
return results
|
||||
|
||||
def list_docs(self, tag: str = None, status: str = None, limit: int = None) -> List[Dict]:
|
||||
"""
|
||||
列出文档
|
||||
|
||||
Args:
|
||||
tag: 按标签筛选
|
||||
status: 按状态筛选
|
||||
limit: 限制数量
|
||||
|
||||
Returns:
|
||||
文档列表
|
||||
"""
|
||||
docs = self._parse_table()
|
||||
|
||||
# 筛选
|
||||
if tag:
|
||||
docs = [d for d in docs if tag in d.get("tags", "")]
|
||||
|
||||
if status:
|
||||
docs = [d for d in docs if d.get("status") == status]
|
||||
|
||||
# 限制数量
|
||||
if limit:
|
||||
docs = docs[:limit]
|
||||
|
||||
return docs
|
||||
|
||||
def get_doc_by_token(self, token: str) -> Optional[Dict]:
|
||||
"""通过token获取文档信息"""
|
||||
docs = self._parse_table()
|
||||
for doc in docs:
|
||||
if token in doc.get("link", ""):
|
||||
return doc
|
||||
return None
|
||||
|
||||
def _parse_table(self) -> List[Dict]:
|
||||
"""解析索引表格"""
|
||||
docs = []
|
||||
|
||||
try:
|
||||
with open(self.index_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 找到表格部分
|
||||
lines = content.split('\n')
|
||||
in_table = False
|
||||
|
||||
for line in lines:
|
||||
if line.startswith('| 序号 '):
|
||||
in_table = True
|
||||
continue
|
||||
if in_table and line.startswith('|---'):
|
||||
continue
|
||||
if in_table and line.startswith('|') and not line.startswith('|------'):
|
||||
# 解析表格行
|
||||
parts = [p.strip() for p in line.split('|')[1:-1]]
|
||||
if len(parts) >= 8:
|
||||
# 兼容8列表格格式(序号、文档名、类型、链接、摘要、状态、更新时间、备注)
|
||||
docs.append({
|
||||
"index": parts[0],
|
||||
"name": parts[1],
|
||||
"type": parts[2],
|
||||
"link": parts[3],
|
||||
"summary": parts[4],
|
||||
"status": parts[5],
|
||||
"updated": parts[6],
|
||||
"tags": parts[7], # 使用"备注"列作为标签
|
||||
"owner": "" # 所有者信息暂时为空
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"解析索引失败: {e}")
|
||||
|
||||
return docs
|
||||
|
||||
def _find_doc_index(self, content: str, token: str) -> Optional[str]:
|
||||
"""查找文档是否已存在,返回序号"""
|
||||
pattern = r'\| (\d+) \| [^|]+ \| [^|]+ \| [^/]+/docx/' + re.escape(token) + r'[^|]* \|'
|
||||
match = re.search(pattern, content)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
def _get_next_index(self, content: str) -> int:
|
||||
"""获取下一个序号"""
|
||||
pattern = r'\| (\d+) \|'
|
||||
matches = re.findall(pattern, content)
|
||||
if matches:
|
||||
return max([int(m) for m in matches]) + 1
|
||||
return 1
|
||||
|
||||
def _replace_table_row(self, content: str, token: str, new_row: str) -> str:
|
||||
"""替换表格中的某一行"""
|
||||
pattern = r'(\| \d+ \| [^|]+ \| [^|]+ \| [^/]+/docx/' + re.escape(token) + r'[^|]* \|[^\n]+)'
|
||||
return re.sub(pattern, new_row, content)
|
||||
|
||||
def _insert_table_row(self, content: str, new_row: str) -> str:
|
||||
"""插入新行到表格"""
|
||||
# 在表格头部后插入
|
||||
lines = content.split('\n')
|
||||
insert_index = -1
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith('| 序号 '):
|
||||
insert_index = i + 2 # 跳过表头和分隔线
|
||||
break
|
||||
|
||||
if insert_index > 0:
|
||||
lines.insert(insert_index, new_row)
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
def _update_categories(self, content: str, name: str, tags: List[str]) -> str:
|
||||
"""更新分类列表"""
|
||||
# 简单实现:在对应分类下添加文档名
|
||||
# 这里可以扩展更复杂的逻辑
|
||||
return content
|
||||
|
||||
def _update_keywords(self, content: str, name: str, summary: str, tags: List[str]) -> str:
|
||||
"""更新关键词索引"""
|
||||
# 提取关键词
|
||||
keywords = []
|
||||
if tags:
|
||||
keywords.extend(tags)
|
||||
|
||||
# 这里可以添加更智能的关键词提取
|
||||
# 暂时保持简单
|
||||
|
||||
return content
|
||||
|
||||
def _update_last_modified(self, content: str) -> str:
|
||||
"""更新最后修改时间"""
|
||||
now = datetime.now().strftime("%Y-%m-%d")
|
||||
pattern = r'\*最后更新:[^*]+\*'
|
||||
replacement = f'*最后更新:{now}*'
|
||||
return re.sub(pattern, replacement, content)
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def add_doc_to_index(name: str, url: str, token: str, summary: str = "",
|
||||
tags: List[str] = None, owner: str = "") -> bool:
|
||||
"""便捷函数:添加文档到索引"""
|
||||
manager = IndexManager()
|
||||
return manager.add_or_update_doc(
|
||||
name=name,
|
||||
doc_type="docx",
|
||||
url=url,
|
||||
token=token,
|
||||
summary=summary,
|
||||
status="已完成",
|
||||
tags=tags,
|
||||
owner=owner
|
||||
)
|
||||
|
||||
|
||||
def search_docs(keyword: str) -> List[Dict]:
|
||||
"""便捷函数:搜索文档"""
|
||||
manager = IndexManager()
|
||||
return manager.search_docs(keyword)
|
||||
|
||||
|
||||
def list_all_docs(tag: str = None) -> List[Dict]:
|
||||
"""便捷函数:列出所有文档"""
|
||||
manager = IndexManager()
|
||||
return manager.list_docs(tag=tag)
|
||||
Reference in New Issue
Block a user