Publish emlog via gitea-publish skill
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
---
|
||||
name: emlog
|
||||
description: 发布文章到 emlog 博客。当用户提到「发文章」「发布到博客」「blog」「emlog」「article_post」「微语笔记」等时使用。基于 emlog REST API,支持文章发布、文章列表、分类查询、草稿管理、微语笔记等功能。
|
||||
---
|
||||
|
||||
# emlog 博客发布 Skill
|
||||
|
||||
通过 emlog REST API 与博客系统交互,支持发布文章、查询文章、获取分类、管理草稿、发布微语等。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 博客需开启 API 功能(后台:系统-设置-API 设置)
|
||||
- 需要 API 秘钥(后台可查看)
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `EMLOG_BASE_URL` | 博客站点地址,如 `https://blog.apescale.com` | `https://blog.apescale.com` |
|
||||
| `EMLOG_API_KEY` | API 秘钥(后台系统-设置-API 设置) | 无 |
|
||||
|
||||
## 使用方式
|
||||
|
||||
```bash
|
||||
# 发布文章(推荐:内容较长时用文件传参)
|
||||
python3 scripts/emlog.py post \
|
||||
--title "文章标题" \
|
||||
--content-file /tmp/article.md \
|
||||
--sort-id 14 \
|
||||
--tags "QwenPaw,TencentDB"
|
||||
|
||||
# 发布文章(内容较短时直接传)
|
||||
python3 scripts/emlog.py post --title "标题" --content "正文内容" --sort-id 14
|
||||
|
||||
# 获取分类列表
|
||||
python3 scripts/emlog.py sorts
|
||||
|
||||
# 文章列表(可按分类过滤)
|
||||
python3 scripts/emlog.py list --sort-id 14 --count 10
|
||||
|
||||
# 文章详情
|
||||
python3 scripts/emlog.py detail --id 123
|
||||
|
||||
# 草稿列表
|
||||
python3 scripts/emlog.py drafts
|
||||
|
||||
# 发布微语笔记
|
||||
python3 scripts/emlog.py note --text "这是一条微语"
|
||||
```
|
||||
|
||||
## 鉴权方式
|
||||
|
||||
脚本默认使用**免签名鉴权**(传 `api_key` 参数),简单可靠,建议配合 HTTPS 使用。
|
||||
如需签名鉴权,可传 `--sign` 参数(自动计算 `md5(req_time + api_key)`)。
|
||||
|
||||
## 发布参数说明(article_post)
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `title` | ✅ | 标题 |
|
||||
| `content` | ✅ | 内容(支持 Markdown,emlog 会保存 raw 原文) |
|
||||
| `excerpt` | ⬜ | 摘要 |
|
||||
| `sort_id` | ⬜ | 分类 ID(`emlog.py sorts` 可查) |
|
||||
| `tags` | ⬜ | 标签,半角逗号分隔 |
|
||||
| `draft` | ⬜ | `y` 存草稿 / `n` 发布(默认 `n`) |
|
||||
| `post_date` | ⬜ | 发布时间 `2026-08-08 12:00:00` |
|
||||
| `alias` | ⬜ | 文章别名 |
|
||||
| `top` | ⬜ | 首页置顶 `y`/`n` |
|
||||
| `allow_remark` | ⬜ | 允许评论 `y`/`n` |
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 返回 msg | 含义 | 处理 |
|
||||
|----------|------|------|
|
||||
| `sign error` | 签名错误 | 检查 API 秘钥 / 时间戳 |
|
||||
| `api is closed` | 未开启 API | 后台系统-设置-API 设置开启 |
|
||||
| `API function is not exist` | 接口名错误 | 检查 `rest-api` 参数 |
|
||||
| `parameter error` | 必填参数缺失 | 检查 title/content |
|
||||
|
||||
## 返回格式
|
||||
|
||||
```json
|
||||
{ "code": 0, "msg": "ok", "data": { "article_id": 14 } }
|
||||
```
|
||||
|
||||
`code=0` 表示成功,`code=1` 表示失败(看 `msg`)。
|
||||
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""emlog REST API 客户端 — 发布文章/查询/分类/草稿/微语。
|
||||
|
||||
用法示例:
|
||||
python3 emlog.py post --title "标题" --content "正文" --sort-id 14 --tags "a,b"
|
||||
python3 emlog.py post --title "标题" --content-file /tmp/a.md --sort-id 14
|
||||
python3 emlog.py sorts
|
||||
python3 emlog.py list --sort-id 14 --count 10
|
||||
python3 emlog.py detail --id 123
|
||||
python3 emlog.py drafts
|
||||
python3 emlog.py note --text "微语内容"
|
||||
|
||||
鉴权:
|
||||
默认免签名(api_key);加 --sign 走签名鉴权 md5(req_time + api_key)
|
||||
环境变量:
|
||||
EMLOG_BASE_URL 默认 https://blog.apescale.com
|
||||
EMLOG_API_KEY API 秘钥
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
BASE_URL = os.environ.get("EMLOG_BASE_URL", "https://blog.apescale.com")
|
||||
API_KEY = os.environ.get("EMLOG_API_KEY", "")
|
||||
|
||||
|
||||
def request(rest_api, params, method="POST"):
|
||||
url = f"{BASE_URL}/?rest-api={rest_api}"
|
||||
data = urllib.parse.urlencode(params).encode("utf-8")
|
||||
req = urllib.request.Request(url, data=data, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
except urllib.error.HTTPError as e:
|
||||
body = e.read().decode("utf-8", "ignore")
|
||||
try:
|
||||
return json.loads(body)
|
||||
except Exception:
|
||||
return {"code": 1, "msg": f"HTTP {e.code}: {body}"}
|
||||
except Exception as e:
|
||||
return {"code": 1, "msg": str(e)}
|
||||
|
||||
|
||||
def build_auth_params(args):
|
||||
p = {}
|
||||
if args.sign:
|
||||
t = str(int(time.time()))
|
||||
p["req_time"] = t
|
||||
p["req_sign"] = hashlib.md5((t + args.api_key).encode()).hexdigest()
|
||||
else:
|
||||
p["api_key"] = args.api_key
|
||||
return p
|
||||
|
||||
|
||||
def cmd_post(args):
|
||||
content = args.content
|
||||
if args.content_file:
|
||||
with open(args.content_file, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
params = {
|
||||
"title": args.title,
|
||||
"content": content,
|
||||
"sort_id": args.sort_id,
|
||||
"tags": args.tags,
|
||||
"draft": args.draft,
|
||||
"excerpt": args.excerpt,
|
||||
"alias": args.alias,
|
||||
"top": args.top,
|
||||
"allow_remark": args.allow_remark,
|
||||
}
|
||||
params = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
params.update(build_auth_params(args))
|
||||
if args.id:
|
||||
# 更新已有文章:emlog 使用 article_update 接口(id 必填)
|
||||
params = {"id": args.id, **params}
|
||||
return request("article_update", params)
|
||||
return request("article_post", params)
|
||||
|
||||
|
||||
def cmd_list(args):
|
||||
params = {"count": args.count}
|
||||
if args.sort_id is not None:
|
||||
params["sort_id"] = args.sort_id
|
||||
if args.keyword:
|
||||
params["keyword"] = args.keyword
|
||||
if args.tag:
|
||||
params["tag"] = args.tag
|
||||
return request("article_list", params, method="GET")
|
||||
|
||||
|
||||
def cmd_sorts(args):
|
||||
return request("sort_list", {}, method="GET")
|
||||
|
||||
|
||||
def cmd_detail(args):
|
||||
return request("article_detail", {"id": args.id}, method="GET")
|
||||
|
||||
|
||||
def cmd_drafts(args):
|
||||
params = {}
|
||||
if args.count:
|
||||
params["count"] = args.count
|
||||
params.update(build_auth_params(args))
|
||||
return request("draft_list", params)
|
||||
|
||||
|
||||
def cmd_note(args):
|
||||
params = {"t": args.text}
|
||||
params.update(build_auth_params(args))
|
||||
return request("note_post", params)
|
||||
|
||||
|
||||
def main():
|
||||
global BASE_URL, API_KEY
|
||||
parser = argparse.ArgumentParser(description="emlog REST API 客户端")
|
||||
parser.add_argument("--base-url", default=BASE_URL)
|
||||
parser.add_argument("--api-key", default=API_KEY)
|
||||
parser.add_argument("--sign", action="store_true", help="使用签名鉴权")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
p = sub.add_parser("post", help="发布/更新文章")
|
||||
p.add_argument("--id", default=None, help="文章 ID(更新已有文章时传入)")
|
||||
p.add_argument("--title", required=True)
|
||||
p.add_argument("--content", default="")
|
||||
p.add_argument("--content-file", default=None)
|
||||
p.add_argument("--sort-id", default=None)
|
||||
p.add_argument("--tags", default="")
|
||||
p.add_argument("--draft", default="n")
|
||||
p.add_argument("--excerpt", default="")
|
||||
p.add_argument("--alias", default="")
|
||||
p.add_argument("--top", default="n")
|
||||
p.add_argument("--allow-remark", default="n")
|
||||
p.set_defaults(func=cmd_post)
|
||||
|
||||
p = sub.add_parser("list", help="文章列表")
|
||||
p.add_argument("--sort-id", type=int, default=None)
|
||||
p.add_argument("--count", type=int, default=10)
|
||||
p.add_argument("--keyword", default="")
|
||||
p.add_argument("--tag", default="")
|
||||
p.set_defaults(func=cmd_list)
|
||||
|
||||
p = sub.add_parser("sorts", help="分类列表")
|
||||
p.set_defaults(func=cmd_sorts)
|
||||
|
||||
p = sub.add_parser("detail", help="文章详情")
|
||||
p.add_argument("--id", required=True)
|
||||
p.set_defaults(func=cmd_detail)
|
||||
|
||||
p = sub.add_parser("drafts", help="草稿列表")
|
||||
p.add_argument("--count", type=int, default=None)
|
||||
p.set_defaults(func=cmd_drafts)
|
||||
|
||||
p = sub.add_parser("note", help="发布微语")
|
||||
p.add_argument("--text", required=True)
|
||||
p.set_defaults(func=cmd_note)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.base_url = args.base_url or BASE_URL
|
||||
args.api_key = args.api_key or API_KEY
|
||||
BASE_URL, API_KEY = args.base_url, args.api_key
|
||||
|
||||
result = args.func(args)
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
if result.get("code") != 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user