176 lines
5.4 KiB
Python
176 lines
5.4 KiB
Python
#!/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()
|