feat: 将 47 个 Operit TypeScript 脚本全面翻译为 Python
- 新增 operit_runtime.py: 跨平台 Python 运行时 - 实现 Tools.System/Files/Net/UI/Memory/FFmpeg/Workflow/Chat/SoftwareSettings/Tasker - 实现 OkHttp 链式 HTTP 客户端兼容层 - 实现 complete()/exports/console 全局对象 - 实现 OperitScriptLoader 脚本加载器 - 支持 Windows/macOS/Linux 三平台 - 新增 operit_py/ 目录: 47 个 Python 翻译脚本 - 搜索类: duckduckgo, google_search, tavily, zhipu_search, various_search, crossref - 绘图类: openai_draw, qwen_draw, xai_draw, minimax_draw, siliconflow_draw, zhipu_draw, nanobanana_draw - 媒体类: ffmpeg, file_converter, douyin_download, bilibili_tools, jmcomic - 文件类: extended_file_tools, hex_editor, reader, beeimg_image_uploader - 网络类: extended_http_tools, network_test - 自动化类: browser, automatic_ui_base, automatic_ui_subagent, automatic_bilibili_assistant, automatic_xiaohongshu_assistant, automatic_baidu_map_assistant - 系统类: system_tools, super_admin, daily_life, operit_editor - 对话类: ai_chat, extended_chat, qq_intelligent - 工作流类: workflow, vflow_trigger, tasker - 工具类: time, code_runner, quick_start, extended_memory_tools, baidu_map, 12306, pdf_vision_parser - 测试结果: 47/47 语法检查通过, 38 脚本加载成功, 280 个工具注册 - Android 专属功能已降级处理(保留接口,返回不支持提示)
This commit is contained in:
1 parent
6fa759665d
commit
dc77dfd304
48 files changed
+22273
No files matched your search
@@ -0,0 +1,515 @@
|
||||
# METADATA
|
||||
# {
|
||||
# "name": "crossref",
|
||||
# "display_name": {
|
||||
# "zh": "Crossref 学术文献查询",
|
||||
# "en": "Crossref Academic Literature Search"
|
||||
# },
|
||||
# "description": {
|
||||
# "zh": "Crossref 学术文献查询工具,提供 DOI 查询、关键词搜索、作者搜索等功能,帮助用户查找和获取学术文章元数据。",
|
||||
# "en": "Crossref scholarly literature search tools: query by DOI, keyword, author, title, ISSN, and retrieve publication metadata."
|
||||
# },
|
||||
# "category": "Search",
|
||||
# "enabledByDefault": true,
|
||||
# "tools": [
|
||||
# {
|
||||
# "name": "search_by_doi",
|
||||
# "description": { "zh": "通过 DOI (Digital Object Identifier) 查询文章的详细信息", "en": "Query article details by DOI (Digital Object Identifier)." },
|
||||
# "parameters": [
|
||||
# { "name": "doi", "description": { "zh": "文章的 DOI 标识符,例如 '10.1038/nature12373'", "en": "DOI identifier, e.g. '10.1038/nature12373'" }, "type": "string", "required": true }
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# "name": "search_by_keyword",
|
||||
# "description": { "zh": "通过关键词搜索学术文章", "en": "Search scholarly articles by keyword." },
|
||||
# "parameters": [
|
||||
# { "name": "query", "description": { "zh": "搜索关键词", "en": "Search query keyword(s)" }, "type": "string", "required": true },
|
||||
# { "name": "rows", "description": { "zh": "返回结果数量,默认 10,最大 100", "en": "Number of results to return (default: 10, max: 100)" }, "type": "number", "required": false },
|
||||
# { "name": "sort", "description": { "zh": "排序方式,可选值:'relevance'(相关性), 'score'(评分), 'updated'(更新时间), 'deposited'(提交时间), 'indexed'(索引时间), 'published'(发布时间)", "en": "Sort mode. Options: 'relevance', 'score', 'updated', 'deposited', 'indexed', 'published'." }, "type": "string", "required": false },
|
||||
# { "name": "order", "description": { "zh": "排序顺序,可选值:'asc'(升序), 'desc'(降序),默认 'desc'", "en": "Sort order: 'asc' or 'desc' (default: 'desc')." }, "type": "string", "required": false }
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# "name": "search_by_author",
|
||||
# "description": { "zh": "通过作者名字搜索文章", "en": "Search articles by author name." },
|
||||
# "parameters": [
|
||||
# { "name": "author", "description": { "zh": "作者名字", "en": "Author name" }, "type": "string", "required": true },
|
||||
# { "name": "rows", "description": { "zh": "返回结果数量,默认 10,最大 100", "en": "Number of results to return (default 10, max 100)" }, "type": "number", "required": false }
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# "name": "search_by_title",
|
||||
# "description": { "zh": "通过文章标题搜索", "en": "Search articles by title." },
|
||||
# "parameters": [
|
||||
# { "name": "title", "description": { "zh": "文章标题或标题关键词", "en": "Article title or title keyword(s)" }, "type": "string", "required": true },
|
||||
# { "name": "rows", "description": { "zh": "返回结果数量,默认 10,最大 100", "en": "Number of results to return (default 10, max 100)" }, "type": "number", "required": false }
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# "name": "search_by_issn",
|
||||
# "description": { "zh": "通过期刊 ISSN 查询该期刊发表的文章", "en": "Search articles published in a journal by ISSN." },
|
||||
# "parameters": [
|
||||
# { "name": "issn", "description": { "zh": "期刊的 ISSN 标识符,例如 '1476-4687'", "en": "Journal ISSN identifier, e.g. '1476-4687'" }, "type": "string", "required": true },
|
||||
# { "name": "rows", "description": { "zh": "返回结果数量,默认 10,最大 100", "en": "Number of results to return (default 10, max 100)" }, "type": "number", "required": false }
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
|
||||
import json
|
||||
import re
|
||||
from urllib.parse import quote
|
||||
|
||||
BASE_URL = "https://api.crossref.org"
|
||||
DEFAULT_ROWS = 10
|
||||
MAX_ROWS = 100
|
||||
|
||||
|
||||
def buildQueryString(params):
|
||||
return "&".join(f"{quote(str(key))}={quote(str(value))}" for key, value in params.items())
|
||||
|
||||
|
||||
# 格式化作者信息
|
||||
def formatAuthors(authors):
|
||||
if not authors or len(authors) == 0:
|
||||
return "N/A"
|
||||
names = []
|
||||
for author in authors[:5]: # 只显示前5个作者
|
||||
given = author.get("given") or ""
|
||||
family = author.get("family") or ""
|
||||
name = f"{given} {family}".strip()
|
||||
if len(name) > 0:
|
||||
names.append(name)
|
||||
return ", ".join(names)
|
||||
|
||||
|
||||
# 格式化日期
|
||||
def formatDate(dateParts):
|
||||
if not dateParts or len(dateParts) == 0 or not dateParts[0]:
|
||||
return "N/A"
|
||||
parts = dateParts[0]
|
||||
if len(parts) == 1:
|
||||
return f"{parts[0]}"
|
||||
if len(parts) == 2:
|
||||
return f"{parts[0]}-{str(parts[1]).rjust(2, '0')}"
|
||||
if len(parts) == 3:
|
||||
return f"{parts[0]}-{str(parts[1]).rjust(2, '0')}-{str(parts[2]).rjust(2, '0')}"
|
||||
return "N/A"
|
||||
|
||||
|
||||
def _get(obj, key, default=None):
|
||||
if isinstance(obj, dict):
|
||||
return obj.get(key, default)
|
||||
return default
|
||||
|
||||
|
||||
# 格式化单篇文章信息
|
||||
def formatArticle(item, index=None):
|
||||
lines = []
|
||||
|
||||
if index is not None:
|
||||
lines.append(f"\n=== Article {index + 1} ===")
|
||||
|
||||
# 标题
|
||||
title = (item.get("title")[0] if item.get("title") and len(item.get("title")) > 0 else "No Title")
|
||||
lines.append(f"Title: {title}")
|
||||
|
||||
# DOI
|
||||
if item.get("DOI"):
|
||||
lines.append(f"DOI: {item['DOI']}")
|
||||
lines.append(f"URL: https://doi.org/{item['DOI']}")
|
||||
|
||||
# 作者
|
||||
authors = formatAuthors(item.get("author"))
|
||||
lines.append(f"Authors: {authors}")
|
||||
|
||||
# 发表日期
|
||||
published = _get(item, "published") or {}
|
||||
publishedPrint = _get(item, "published-print") or {}
|
||||
publishedOnline = _get(item, "published-online") or {}
|
||||
publishedDate = formatDate(
|
||||
_get(published, "date-parts") or _get(publishedPrint, "date-parts") or _get(publishedOnline, "date-parts")
|
||||
)
|
||||
lines.append(f"Published: {publishedDate}")
|
||||
|
||||
# 期刊/会议
|
||||
containerTitle = item.get("container-title")
|
||||
if containerTitle and len(containerTitle) > 0:
|
||||
lines.append(f"Journal/Conference: {containerTitle[0]}")
|
||||
|
||||
# ISSN
|
||||
issn = item.get("ISSN")
|
||||
if issn and len(issn) > 0:
|
||||
lines.append(f"ISSN: {', '.join(issn)}")
|
||||
|
||||
# 出版商
|
||||
if item.get("publisher"):
|
||||
lines.append(f"Publisher: {item['publisher']}")
|
||||
|
||||
# 类型
|
||||
if item.get("type"):
|
||||
lines.append(f"Type: {item['type']}")
|
||||
|
||||
# 引用次数
|
||||
if item.get("is-referenced-by-count") is not None:
|
||||
lines.append(f"Citations: {item['is-referenced-by-count']}")
|
||||
|
||||
# 摘要(如果有)
|
||||
if item.get("abstract"):
|
||||
# 移除 HTML 标签
|
||||
abstractText = re.sub(r"<[^>]*>", "", item["abstract"])
|
||||
lines.append(f"Abstract: {abstractText[:500]}{'...' if len(abstractText) > 500 else ''}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
async def _httpGet(url, headers):
|
||||
client = OkHttp.newBuilder().build()
|
||||
response = await client.newRequest().url(url).method("GET").headers(headers).build().execute()
|
||||
return response
|
||||
|
||||
|
||||
# 通过 DOI 查询文章
|
||||
async def searchByDoi(params):
|
||||
doi = params.get("doi")
|
||||
|
||||
if not doi or doi.strip() == "":
|
||||
return {
|
||||
"success": False,
|
||||
"message": "请提供有效的 DOI"
|
||||
}
|
||||
|
||||
try:
|
||||
url = f"{BASE_URL}/works/{quote(doi, safe='')}"
|
||||
response = await _httpGet(url, {
|
||||
"User-Agent": "Operit/1.0 (mailto:support@example.com)"
|
||||
})
|
||||
|
||||
if not response.isSuccessful():
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"查询失败: HTTP {response.statusCode} - {getattr(response, 'statusMessage', '')}"
|
||||
}
|
||||
|
||||
data = json.loads(response.content)
|
||||
|
||||
if data.get("status") == "ok" and data.get("message"):
|
||||
article = formatArticle(data["message"])
|
||||
return {
|
||||
"success": True,
|
||||
"message": "查询成功",
|
||||
"data": article
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "未找到该 DOI 对应的文章"
|
||||
}
|
||||
except Exception as error:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"查询失败: {str(error)}"
|
||||
}
|
||||
|
||||
|
||||
# 通过关键词搜索文章
|
||||
async def searchByKeyword(params):
|
||||
query = params.get("query")
|
||||
rows = params.get("rows") if params.get("rows") is not None else DEFAULT_ROWS
|
||||
sort = params.get("sort") if params.get("sort") is not None else "relevance"
|
||||
order = params.get("order") if params.get("order") is not None else "desc"
|
||||
|
||||
if not query or query.strip() == "":
|
||||
return {
|
||||
"success": False,
|
||||
"message": "请提供有效的搜索关键词"
|
||||
}
|
||||
|
||||
actualRows = min(max(int(rows), 1), MAX_ROWS)
|
||||
|
||||
try:
|
||||
queryString = buildQueryString({
|
||||
"query": query,
|
||||
"rows": str(actualRows),
|
||||
"sort": sort,
|
||||
"order": order
|
||||
})
|
||||
|
||||
url = f"{BASE_URL}/works?{queryString}"
|
||||
response = await _httpGet(url, {
|
||||
"User-Agent": "Operit/1.0 (mailto:support@example.com)"
|
||||
})
|
||||
|
||||
if not response.isSuccessful():
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"搜索失败: HTTP {response.statusCode} - {getattr(response, 'statusMessage', '')}"
|
||||
}
|
||||
|
||||
data = json.loads(response.content)
|
||||
|
||||
if data.get("status") == "ok" and data.get("message") and data["message"].get("items"):
|
||||
items = data["message"]["items"]
|
||||
totalResults = data["message"].get("total-results")
|
||||
|
||||
results = [formatArticle(item, index) for index, item in enumerate(items)]
|
||||
|
||||
summary = f"Found {totalResults} results (showing {len(items)}):\n" + "\n\n".join(results)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "搜索成功",
|
||||
"data": summary,
|
||||
"total": totalResults,
|
||||
"count": len(items)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": "未找到相关文章"
|
||||
}
|
||||
except Exception as error:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"搜索失败: {str(error)}"
|
||||
}
|
||||
|
||||
|
||||
# 通过作者搜索文章
|
||||
async def searchByAuthor(params):
|
||||
author = params.get("author")
|
||||
rows = params.get("rows") if params.get("rows") is not None else DEFAULT_ROWS
|
||||
|
||||
if not author or author.strip() == "":
|
||||
return {
|
||||
"success": False,
|
||||
"message": "请提供有效的作者名字"
|
||||
}
|
||||
|
||||
actualRows = min(max(int(rows), 1), MAX_ROWS)
|
||||
|
||||
try:
|
||||
queryString = buildQueryString({
|
||||
"query.author": author,
|
||||
"rows": str(actualRows)
|
||||
})
|
||||
|
||||
url = f"{BASE_URL}/works?{queryString}"
|
||||
response = await _httpGet(url, {
|
||||
"User-Agent": "Operit/1.0 (mailto:support@example.com)"
|
||||
})
|
||||
|
||||
if not response.isSuccessful():
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"搜索失败: HTTP {response.statusCode} - {getattr(response, 'statusMessage', '')}"
|
||||
}
|
||||
|
||||
data = json.loads(response.content)
|
||||
|
||||
if data.get("status") == "ok" and data.get("message") and data["message"].get("items"):
|
||||
items = data["message"]["items"]
|
||||
totalResults = data["message"].get("total-results")
|
||||
|
||||
results = [formatArticle(item, index) for index, item in enumerate(items)]
|
||||
|
||||
summary = f"Found {totalResults} results for author \"{author}\" (showing {len(items)}):\n" + "\n\n".join(results)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "搜索成功",
|
||||
"data": summary,
|
||||
"total": totalResults,
|
||||
"count": len(items)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"未找到该作者 \"{author}\" 的文章"
|
||||
}
|
||||
except Exception as error:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"搜索失败: {str(error)}"
|
||||
}
|
||||
|
||||
|
||||
# 通过标题搜索文章
|
||||
async def searchByTitle(params):
|
||||
title = params.get("title")
|
||||
rows = params.get("rows") if params.get("rows") is not None else DEFAULT_ROWS
|
||||
|
||||
if not title or title.strip() == "":
|
||||
return {
|
||||
"success": False,
|
||||
"message": "请提供有效的文章标题"
|
||||
}
|
||||
|
||||
actualRows = min(max(int(rows), 1), MAX_ROWS)
|
||||
|
||||
try:
|
||||
queryString = buildQueryString({
|
||||
"query.title": title,
|
||||
"rows": str(actualRows)
|
||||
})
|
||||
|
||||
url = f"{BASE_URL}/works?{queryString}"
|
||||
response = await _httpGet(url, {
|
||||
"User-Agent": "Operit/1.0 (mailto:support@example.com)"
|
||||
})
|
||||
|
||||
data = json.loads(response.content)
|
||||
|
||||
if data.get("status") == "ok" and data.get("message") and data["message"].get("items"):
|
||||
items = data["message"]["items"]
|
||||
totalResults = data["message"].get("total-results")
|
||||
|
||||
results = [formatArticle(item, index) for index, item in enumerate(items)]
|
||||
|
||||
summary = f"Found {totalResults} results matching title \"{title}\" (showing {len(items)}):\n" + "\n\n".join(results)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "搜索成功",
|
||||
"data": summary,
|
||||
"total": totalResults,
|
||||
"count": len(items)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"未找到标题包含 \"{title}\" 的文章"
|
||||
}
|
||||
except Exception as error:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"搜索失败: {str(error)}"
|
||||
}
|
||||
|
||||
|
||||
# 通过 ISSN 查询期刊文章
|
||||
async def searchByISSN(params):
|
||||
issn = params.get("issn")
|
||||
rows = params.get("rows") if params.get("rows") is not None else DEFAULT_ROWS
|
||||
|
||||
if not issn or issn.strip() == "":
|
||||
return {
|
||||
"success": False,
|
||||
"message": "请提供有效的 ISSN"
|
||||
}
|
||||
|
||||
actualRows = min(max(int(rows), 1), MAX_ROWS)
|
||||
|
||||
try:
|
||||
url = f"{BASE_URL}/journals/{quote(issn, safe='')}/works?rows={actualRows}"
|
||||
response = await _httpGet(url, {
|
||||
"User-Agent": "Operit/1.0 (mailto:support@example.com)"
|
||||
})
|
||||
|
||||
if not response.isSuccessful():
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"查询失败: HTTP {response.statusCode} - {getattr(response, 'statusMessage', '')}"
|
||||
}
|
||||
|
||||
data = json.loads(response.content)
|
||||
|
||||
if data.get("status") == "ok" and data.get("message") and data["message"].get("items"):
|
||||
items = data["message"]["items"]
|
||||
totalResults = data["message"].get("total-results")
|
||||
|
||||
results = [formatArticle(item, index) for index, item in enumerate(items)]
|
||||
|
||||
summary = f"Found {totalResults} articles from journal ISSN {issn} (showing {len(items)}):\n" + "\n\n".join(results)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "查询成功",
|
||||
"data": summary,
|
||||
"total": totalResults,
|
||||
"count": len(items)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"未找到 ISSN \"{issn}\" 对应期刊的文章"
|
||||
}
|
||||
except Exception as error:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"查询失败: {str(error)}"
|
||||
}
|
||||
|
||||
|
||||
# 包装函数,统一处理错误
|
||||
async def wrapToolExecution(func, params):
|
||||
try:
|
||||
result = await func(params)
|
||||
complete(result)
|
||||
except Exception as error:
|
||||
console.error("工具执行失败", error)
|
||||
complete({
|
||||
"success": False,
|
||||
"message": f"工具执行时发生意外错误: {str(error)}",
|
||||
})
|
||||
|
||||
|
||||
# 测试函数
|
||||
async def main(params=None):
|
||||
if params is None:
|
||||
params = {}
|
||||
console.log("=== Crossref API 测试 ===\n")
|
||||
|
||||
# 测试 1: 通过 DOI 查询
|
||||
console.log("1. 测试通过 DOI 查询...")
|
||||
doiResult = await searchByDoi({"doi": "10.1038/nature12373"})
|
||||
console.log(json.dumps(doiResult, ensure_ascii=False, indent=2))
|
||||
console.log("\n")
|
||||
|
||||
# 测试 2: 通过关键词搜索
|
||||
console.log("2. 测试通过关键词搜索...")
|
||||
keywordResult = await searchByKeyword({"query": "machine learning", "rows": 3})
|
||||
console.log(json.dumps(keywordResult, ensure_ascii=False, indent=2))
|
||||
console.log("\n")
|
||||
|
||||
# 测试 3: 通过作者搜索
|
||||
console.log("3. 测试通过作者搜索...")
|
||||
authorResult = await searchByAuthor({"author": "John Smith", "rows": 3})
|
||||
console.log(json.dumps(authorResult, ensure_ascii=False, indent=2))
|
||||
console.log("\n")
|
||||
|
||||
# 测试 4: 通过标题搜索
|
||||
console.log("4. 测试通过标题搜索...")
|
||||
titleResult = await searchByTitle({"title": "neural networks", "rows": 3})
|
||||
console.log(json.dumps(titleResult, ensure_ascii=False, indent=2))
|
||||
console.log("\n")
|
||||
|
||||
console.log("=== 测试完成 ===")
|
||||
|
||||
|
||||
async def _exported_search_by_doi(params):
|
||||
await wrapToolExecution(searchByDoi, params)
|
||||
|
||||
|
||||
async def _exported_search_by_keyword(params):
|
||||
await wrapToolExecution(searchByKeyword, params)
|
||||
|
||||
|
||||
async def _exported_search_by_author(params):
|
||||
await wrapToolExecution(searchByAuthor, params)
|
||||
|
||||
|
||||
async def _exported_search_by_title(params):
|
||||
await wrapToolExecution(searchByTitle, params)
|
||||
|
||||
|
||||
async def _exported_search_by_issn(params):
|
||||
await wrapToolExecution(searchByISSN, params)
|
||||
|
||||
|
||||
exports.search_by_doi = _exported_search_by_doi
|
||||
exports.search_by_keyword = _exported_search_by_keyword
|
||||
exports.search_by_author = _exported_search_by_author
|
||||
exports.search_by_title = _exported_search_by_title
|
||||
exports.search_by_issn = _exported_search_by_issn
|
||||
exports.main = main
|
||||
Reference in new issue
Block a user