commit 38b4bc537722161fd29fea53e10a79e718c3bf54 Author: Timmy Date: Tue Mar 17 14:38:54 2026 +0800 Initial commit: Python library for Markdown note services - Add MDAuth class for authentication and session management - Add MDClient class for note operations (CRUD + export/search) - Add CLI tool mdclient_cli.py for quick operations - Support for CodiMD, HedgeDoc, and HackMD services - Multi-endpoint API compatibility Co-Authored-By: Claude Sonnet 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0704f81 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# MDclient 設定檔案範例 +# 複製此檔案為 .env 並填入你的資訊 + +# 伺服器位址 +MDCLIENT_URL=https://codimd.lotimmy.com + +# 登入帳密 +MDCLIENT_EMAIL=your@email.com +MDCLIENT_PASSWORD=your_password + +# Cookie 檔案路徑(可選,預設為 key.conf) +# MDCLIENT_COOKIE_FILE=key.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fd4690 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Environment and credentials +.env +key.conf + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python + +# Virtual environments +venv/ +env/ +ENV/ + +# Build artifacts +mdclient.egg-info/ +dist/ +build/ +*.egg + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/API.md b/API.md new file mode 100644 index 0000000..0423a24 --- /dev/null +++ b/API.md @@ -0,0 +1,255 @@ +# MDclient API 文件 + +## MDAuth + +認證類,用於登入和管理會話。 + +### 建構函式 + +```python +MDAuth(base_url="https://codimd.lotimmy.com", cookie_file="key.conf") +``` + +**參數:** +- `base_url` (str): 伺服器位址 +- `cookie_file` (str): Cookie 儲存檔案路徑 + +### 方法 + +#### login() + +登入並儲存 Cookie。 + +```python +success = auth.login(email, password) +``` + +**參數:** +- `email` (str): 登入信箱 +- `password` (str): 登入密碼 + +**回傳:** +- `bool`: 是否登入成功 + +#### test_cookie() + +測試儲存的 Cookie 是否有效。 + +```python +is_valid = auth.test_cookie() +``` + +**回傳:** +- `bool`: Cookie 是否有效 + +#### load_cookie() + +從檔案載入 Cookie。 + +```python +cookies = auth.load_cookie() +``` + +**回傳:** +- `dict` | `None`: Cookie 字典,檔案不存在時返回 None + +--- + +## MDClient + +客戶端類,提供所有筆記操作的 API。 + +### 建構函式 + +```python +MDClient(base_url="https://codimd.lotimmy.com", cookie_file="key.conf", email=None, password=None) +``` + +**參數:** +- `base_url` (str): 伺服器位址 +- `cookie_file` (str): Cookie 檔案路徑 +- `email` (str): 登入信箱(可選) +- `password` (str): 登入密碼(可選) + +### 方法 + +#### list_notes() + +列出所有筆記。 + +```python +notes = client.list_notes() +``` + +**回傳:** +- `list` | `None`: 筆記列表,失敗時返回 None + +**筆記物件結構:** +```python +{ + "id": "note_id", + "text": "筆記標題", + "time": 1234567890, + "tags": ["tag1", "tag2"], + "pinned": False +} +``` + +#### get_note() + +取得筆記內容。 + +```python +note = client.get_note(note_id) +``` + +**參數:** +- `note_id` (str): 筆記 ID + +**回傳:** +- `dict` | `None`: 筆記內容,失敗時返回 None + +**筆記內容結構:** +```python +{ + "id": "note_id", + "title": "筆記標題", + "content": "Markdown 內容", + "lastChangeAt": "2024-01-01T00:00:00.000Z", + "createdAt": "2024-01-01T00:00:00.000Z" +} +``` + +#### create_note() + +建立新筆記。 + +```python +result = client.create_note(title, content="") +``` + +**參數:** +- `title` (str): 筆記標題 +- `content` (str): Markdown 內容 + +**回傳:** +- `dict` | `None`: 建立結果,失敗時返回 None + +**回傳結構:** +```python +{ + "id": "new_note_id", + "url": "https://server.com/new_note_id" +} +``` + +#### delete_note() + +刪除筆記。 + +```python +success = client.delete_note(note_id) +``` + +**參數:** +- `note_id` (str): 筆記 ID + +**回傳:** +- `bool`: 是否刪除成功 + +#### export_note() + +匯出筆記。 + +```python +content = client.export_note(note_id, format="md") +``` + +**參數:** +- `note_id` (str): 筆記 ID +- `format` (str): 匯出格式(md, pdf, html, slides) + +**回傳:** +- `str` | `None`: 匯出的內容,失敗時返回 None + +#### search_notes() + +搜尋筆記。 + +```python +results = client.search_notes(query) +``` + +**參數:** +- `query` (str): 搜尋關鍵字 + +**回傳:** +- `list` | `None`: 符合的筆記列表 + +--- + +## 使用範例 + +### 完整工作流程 + +```python +from mdclient import MDAuth, MDClient + +# 1. 登入 +auth = MDAuth() +auth.login("user@example.com", "password") + +# 2. 使用客戶端 +client = MDClient() + +# 3. 列出筆記 +notes = client.list_notes() + +# 4. 建立筆記 +result = client.create_note("新筆記", "# 內容") + +# 5. 取得筆記 +note = client.get_note(result['id']) + +# 6. 匯出筆記 +content = client.export_note(result['id']) + +# 7. 刪除筆記 +client.delete_note(result['id']) +``` + +### 錯誤處理 + +```python +from mdclient import MDClient + +client = MDClient() + +# 檢查回傳值 +notes = client.list_notes() +if notes is None: + print("取得筆記失敗") +else: + for note in notes: + print(note['text']) + +# 檢查操作結果 +result = client.create_note("標題", "內容") +if result: + print(f"建立成功: {result['id']}") +else: + print("建立失敗") +``` + +### 自訂伺服器 + +```python +from mdclient import MDAuth, MDClient + +# 連接到不同的伺服器 +auth = MDAuth(base_url="https://demo.hedgedoc.org") +auth.login("user@example.com", "password") + +client = MDClient(base_url="https://demo.hedgedoc.org") +notes = client.list_notes() +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b285fa5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,174 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**mdclient** is a Python library for interacting with Markdown note services (CodiMD, HedgeDoc, HackMD). The project is designed as a Python-first library with an optional CLI tool for quick operations. + +**Key Design Principle:** Library-first architecture - all functionality is exposed through Python APIs (`MDAuth`, `MDClient` classes), with the CLI tool (`mdclient_cli.py`) being a thin wrapper that uses these APIs. + +## Architecture + +### Core Components + +1. **`mdclient/`** - Main Python package + - `MDAuth` class (`auth.py`) - Handles authentication and session management + - `MDClient` class (`client.py`) - All note operations (CRUD + export/search) + - `__init__.py` - Package entry point, exports `MDClient` and `MDAuth` + +2. **`mdclient_cli.py`** - Optional CLI tool + - Standalone script that imports and uses the mdclient package + - Supports .env file integration for credentials + - Not installed as a package command (run directly with `python3 mdclient_cli.py`) + +### Session Management Pattern + +The library uses a cookie-based session system: +- `MDAuth.login()` - Authenticates and saves cookies to a JSON file (`key.conf` by default) +- `MDClient._load_cookie()` - Automatically loads cookies for each request +- Cookies are stored in simple JSON format: `{"connect.sid": "...", "_csrf": "..."}` + +### Multi-Endpoint Strategy + +Both authentication and note operations attempt multiple API endpoints to handle different server implementations: +- Login: `/login`, `/auth/login`, `/api/login`, `/signin` +- Note retrieval: `/api/notes/{id}`, `/api/notes/{id}/content`, `/{id}/download`, `/{id}` +- Note listing: `/api/me/notes`, `/api/notes`, `/history`, `/me/notes` + +This pattern is essential for compatibility across different Markdown note service implementations. + +## Development Commands + +```bash +# Install in development mode +pip install -e ".[dev]" + +# Run tests +pytest + +# Code formatting (line length: 100) +black mdclient/ + +# Type checking +mypy mdclient/ + +# Install with .env support +pip install -e ".[env]" +``` + +## Testing Authentication + +When working on authentication-related features, test with: +```bash +# Test if current cookie is valid +python3 mdclient_cli.py test + +# Login with credentials +python3 mdclient_cli.py login + +# Or use Python directly +python3 -c "from mdclient import MDAuth; MDAuth().test_cookie()" +``` + +## Language and Localization + +**Important:** All user-facing text and documentation must use Traditional Chinese (Taiwan) with natural, direct expression. Avoid translationese and mainland Chinese terminology. + +Common term mappings: +- 登录 → 登入 +- 服务器 → 伺服器 +- 地址 → 位址 +- 保存 → 儲存 +- 文件 → 檔案 +- 导入 → 匯入 +- 导出 → 匯出 +- 创建 → 建立 +- 获取 → 取得 +- 用户 → 使用者 +- 邮箱 → 信箱 +- 请求 → 請求 +- 响应 → 回應 + +## Environment Variables + +The library supports these environment variables (typically set in `.env` file): +- `MDCLIENT_URL` - Server URL +- `MDCLIENT_EMAIL` - Login email +- `MDCLIENT_PASSWORD` - Login password +- `MDCLIENT_COOKIE_FILE` - Cookie file path (default: `key.conf`) + +## Key API Patterns + +When adding new note operations or endpoints: + +1. **Always try multiple endpoints** - Different services use different API patterns +2. **Handle both JSON and text responses** - Some endpoints return HTML/Markdown directly +3. **Return `None` on failure** - Consistent error handling pattern +4. **Print status messages** - Users expect feedback during operations +5. **Use Taiwan Traditional Chinese** - For all user-facing messages + +## Common Patterns + +### Adding a New Note Operation + +```python +def new_operation(self, param: str) -> Optional[Dict]: + """ + 新操作的簡短描述 + + Args: + param: 參數說明 + + Returns: + 操作結果,失敗時返回 None + """ + print(f"正在執行操作: {param}") + + # 嘗試多個可能的端點 + endpoints = ['/api/endpoint1', '/api/endpoint2'] + + for endpoint in endpoints: + response = self._request('GET', endpoint) + if response and response.status_code == 200: + try: + data = response.json() + print("✓ 操作成功") + return data + except json.JSONDecodeError: + # 處理非 JSON 回應 + pass + + print("✗ 操作失敗") + return None +``` + +### CLI Command Pattern + +CLI commands in `mdclient_cli.py` should: +1. Load .env file automatically +2. Support both command-line args and environment variables +3. Use `MDClient` and `MDAuth` classes (no direct HTTP requests) +4. Provide clear success/failure messages + +## Package Structure + +``` +mdclient/ +├── mdclient/ # Core library (Python package) +│ ├── __init__.py # Exports MDClient, MDAuth +│ ├── auth.py # Authentication logic +│ └── client.py # Note operations API +├── mdclient_cli.py # CLI tool (uses mdclient package) +├── pyproject.toml # Package configuration +├── example.py # Usage examples +└── .env.example # Environment variable template +``` + +## Important Notes + +- The library uses `requests` for HTTP operations with `timeout=10` on all requests +- Cookie files are stored as JSON in the current working directory +- The CLI tool is meant for quick operations, not programmatic use +- All methods print status messages to stdout for user feedback +- Error handling returns `None` rather than raising exceptions diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..fac27f1 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,187 @@ +# MDclient 快速開始 + +## 安裝 + +```bash +pip install mdclient +``` + +如果需要 .env 支援: + +```bash +pip install "mdclient[env]" +``` + +## 5 分鐘快速上手 + +### 1. 匯入函式庫 + +```python +from mdclient import MDClient, MDAuth +``` + +### 2. 登入 + +```python +auth = MDAuth() +auth.login("your@email.com", "password") +``` + +### 3. 使用客戶端 + +```python +# 列出筆記 +client = MDClient() +notes = client.list_notes() + +for note in notes: + print(f"{note['text']} - {note['id']}") +``` + +## 常用操作 + +### 建立筆記 + +```python +client = MDClient() +result = client.create_note("我的筆記", "# 內容\n\n這是筆記內容") +``` + +### 取得筆記 + +```python +note = client.get_note(note_id) +print(note['content']) +``` + +### 匯出筆記 + +```python +content = client.export_note(note_id, format="md") +with open("note.md", "w", encoding="utf-8") as f: + f.write(content) +``` + +### 搜尋筆記 + +```python +results = client.search_notes("關鍵字") +``` + +### 刪除筆記 + +```python +client.delete_note(note_id) +``` + +## 使用 .env 檔案(推薦) + +建立 `.env` 檔案: + +```env +MDCLIENT_URL=https://codimd.lotimmy.com +MDCLIENT_EMAIL=your@email.com +MDCLIENT_PASSWORD=your_password +``` + +在程式碼中使用: + +```python +import os +from dotenv import load_dotenv +from mdclient import MDAuth, MDClient + +# 載入環境變數 +load_dotenv() + +# 登入 +auth = MDAuth(base_url=os.getenv('MDCLIENT_URL')) +auth.login( + os.getenv('MDCLIENT_EMAIL'), + os.getenv('MDCLIENT_PASSWORD') +) + +# 使用客戶端 +client = MDClient(base_url=os.getenv('MDCLIENT_URL')) +notes = client.list_notes() +``` + +## 連接到不同的伺服器 + +```python +from mdclient import MDAuth, MDClient + +# 自訂伺服器 +auth = MDAuth(base_url="https://your-server.com") +auth.login("user@example.com", "password") + +client = MDClient(base_url="https://your-server.com") +notes = client.list_notes() +``` + +## 完整範例 + +```python +from mdclient import MDAuth, MDClient + +# 登入 +auth = MDAuth() +auth.login("user@example.com", "password") + +# 建立客戶端 +client = MDClient() + +# 列出筆記 +notes = client.list_notes() +print(f"找到 {len(notes)} 筆筆記") + +# 建立新筆記 +result = client.create_note( + title="測試筆記", + content="# 歡迎使用 MDclient\n\n這是一個測試筆記。" +) +print(f"建立成功: {result['id']}") + +# 取得筆記內容 +note = client.get_note(result['id']) +print(f"筆記標題: {note['title']}") + +# 匯出筆記 +content = client.export_note(result['id']) +with open("backup.md", "w", encoding="utf-8") as f: + f.write(content) +print("筆記已匯出") + +# 刪除筆記 +client.delete_note(result['id']) +print("筆記已刪除") +``` + +## 下一步 + +- 查看 [API.md](API.md) 了解完整的 API 參考 +- 查看 [auth_example.py](auth_example.py) 了解更多認證範例 +- 查看 [example.py](example.py) 了解更多使用範例 + +## 故障排除 + +### Cookie 過期 + +如果遇到認證失敗,重新登入即可: + +```python +auth = MDAuth() +auth.login("email", "password") +``` + +### 連接到不同的伺服器 + +```python +client = MDClient(base_url="https://your-server.com") +``` + +### 自訂 Cookie 檔案 + +```python +auth = MDAuth(cookie_file="custom_cookies.conf") +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..f7af326 --- /dev/null +++ b/README.md @@ -0,0 +1,197 @@ +# MDclient + +Markdown 筆記服務的 Python 客戶端函式庫。 + +支援 CodiMD、HedgeDoc、HackMD 等服務。 + +## 特色 + +- ✅ 簡單好用的 Python API +- ✅ 支援多種 Markdown 筆記服務 +- ✅ 自動會話管理(Cookie) +- ✅ 支援 .env 設定檔 +- ✅ 完整的筆記操作(建立、讀取、更新、刪除、匯出) + +## 安裝 + +```bash +pip install mdclient +``` + +如果需要 .env 支援: + +```bash +pip install "mdclient[env]" +``` + +## 快速開始 + +```python +from mdclient import MDClient, MDAuth + +# 登入 +auth = MDAuth() +auth.login("your@email.com", "password") + +# 使用客戶端 +client = MDClient() +notes = client.list_notes() + +for note in notes: + print(f"{note['text']} - {note['id']}") +``` + +## 主要功能 + +### 列出筆記 + +```python +client = MDClient() +notes = client.list_notes() +``` + +### 建立筆記 + +```python +result = client.create_note("標題", "# 內容") +``` + +### 取得筆記 + +```python +note = client.get_note(note_id) +print(note['content']) +``` + +### 匯出筆記 + +```python +content = client.export_note(note_id, format="md") +``` + +### 搜尋筆記 + +```python +results = client.search_notes("關鍵字") +``` + +### 刪除筆記 + +```python +client.delete_note(note_id) +``` + +## 使用 .env 檔案 + +建立 `.env` 檔案: + +```env +MDCLIENT_URL=https://codimd.lotimmy.com +MDCLIENT_EMAIL=your@email.com +MDCLIENT_PASSWORD=your_password +``` + +在程式碼中使用: + +```python +import os +from dotenv import load_dotenv +from mdclient import MDAuth, MDClient + +load_dotenv() + +auth = MDAuth(base_url=os.getenv('MDCLIENT_URL')) +auth.login( + os.getenv('MDCLIENT_EMAIL'), + os.getenv('MDCLIENT_PASSWORD') +) + +client = MDClient(base_url=os.getenv('MDCLIENT_URL')) +notes = client.list_notes() +``` + +## 自訂伺服器 + +```python +from mdclient import MDAuth, MDClient + +auth = MDAuth(base_url="https://your-server.com") +auth.login("user@example.com", "password") + +client = MDClient(base_url="https://your-server.com") +notes = client.list_notes() +``` + +## CLI 工具(可選) + +專案包含一個可選的 CLI 工具: + +```bash +# 登入 +python3 mdclient_cli.py login + +# 列出筆記 +python3 mdclient_cli.py list + +# 建立筆記 +python3 mdclient_cli.py create "標題" + +# 取得筆記 +python3 mdclient_cli.py get + +# 匯出筆記 +python3 mdclient_cli.py export -o note.md +``` + +## API 參考 + +詳細的 API 文件請查看 [API.md](API.md)。 + +## 範例 + +- [example.py](example.py) - 基本使用範例 +- [auth_example.py](auth_example.py) - 認證功能範例 + +## 專案結構 + +``` +mdclient/ +├── mdclient/ # 核心 Python 函式庫 +│ ├── __init__.py # 匯出 MDClient, MDAuth +│ ├── auth.py # 認證功能 +│ └── client.py # API 客戶端 +├── mdclient_cli.py # 可選的 CLI 工具 +├── example.py # Python 使用範例 +├── auth_example.py # 認證範例 +├── .env.example # 環境變數範例 +├── API.md # API 文件 +├── QUICKSTART.md # 快速開始 +└── README.md # 本檔案 +``` + +## 環境變數 + +- `MDCLIENT_URL` - 伺服器位址 +- `MDCLIENT_EMAIL` - 登入信箱 +- `MDCLIENT_PASSWORD` - 登入密碼 +- `MDCLIENT_COOKIE_FILE` - Cookie 檔案路徑 + +## 開發 + +```bash +# 安裝開發依賴 +pip install -e ".[dev]" + +# 執行測試 +pytest + +# 程式碼格式化 +black mdclient/ + +# 型別檢查 +mypy mdclient/ +``` + +## 授權 + +MIT diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..c38eb54 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,114 @@ +# MDclient 專案概述 + +## 什麼是 MDclient? + +MDclient 是一個 **Python 函式庫**,用於與 Markdown 筆記服務(如 CodiMD、HedgeDoc、HackMD)互動。 + +## 核心特色 + +- **Python 函式庫優先**:主要設計為在 Python 程式碼中使用 +- **簡單的 API**:清晰直觀的介面 +- **多服務支援**:CodiMD、HedgeDoc、HackMD 等 +- **會話管理**:自動儲存和載入 Cookie +- **.env 支援**:方便的設定管理 + +## 專案結構 + +``` +mdclient/ +├── mdclient/ # 核心 Python 函式庫 +│ ├── __init__.py # 匯出 MDClient, MDAuth +│ ├── auth.py # 認證功能 +│ └── client.py # API 客戶端 +├── mdclient_cli.py # 可選的 CLI 工具 +├── example.py # Python 使用範例 +├── auth_example.py # 認證範例 +├── .env.example # 環境變數範例 +├── API.md # API 文件 +├── QUICKSTART.md # 快速開始 +├── README.md # 完整文件 +└── SUMMARY.md # 本檔案 +``` + +## 使用方式 + +### 作為 Python 函式庫(主要) + +```python +from mdclient import MDClient, MDAuth + +# 登入 +auth = MDAuth() +auth.login("email@example.com", "password") + +# 使用客戶端 +client = MDClient() +notes = client.list_notes() +``` + +### 作為 CLI 工具(可選) + +```bash +python3 mdclient_cli.py list +python3 mdclient_cli.py create "標題" +``` + +## 主要 API + +### MDAuth + +```python +from mdclient import MDAuth + +auth = MDAuth(base_url="...", cookie_file="key.conf") +auth.login(email, password) +auth.test_cookie() +``` + +### MDClient + +```python +from mdclient import MDClient + +client = MDClient(base_url="...", cookie_file="key.conf") +client.list_notes() +client.get_note(note_id) +client.create_note(title, content) +client.delete_note(note_id) +client.export_note(note_id, format) +``` + +## 適用場景 + +1. **自動化腳本**:定期備份筆記 +2. **Web 應用**:整合筆記功能 +3. **資料處理**:批次處理筆記 +4. **整合工具**:作為其他工具的依賴 + +## 安裝 + +```bash +pip install mdclient +``` + +## 文件 + +- [README.md](README.md) - 完整文件和安裝指南 +- [API.md](API.md) - 詳細的 API 參考 +- [QUICKSTART.md](QUICKSTART.md) - 快速開始指南 +- [example.py](example.py) - Python 使用範例 +- [auth_example.py](auth_example.py) - 認證功能範例 + +## 設定 + +支援透過 `.env` 檔案設定: + +```env +MDCLIENT_URL=https://codimd.lotimmy.com +MDCLIENT_EMAIL=your@email.com +MDCLIENT_PASSWORD=your_password +``` + +## 總結 + +MDclient 是一個專注於 Python 程式碼中使用的 Markdown 筆記服務客戶端函式庫。如果你需要在 Python 專案中操作 CodiMD、HedgeDoc 等服務,這個函式庫就是為你設計的。 diff --git a/auth_example.py b/auth_example.py new file mode 100644 index 0000000..c64dcf3 --- /dev/null +++ b/auth_example.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +""" +MDclient 認證範例 + +展示如何進行認證和登入 +""" + +from mdclient import MDAuth, MDClient +import os + +def basic_login_example(): + """基本登入範例""" + print("=== 基本登入範例 ===\n") + + # 建立認證物件 + auth = MDAuth( + base_url="https://codimd.lotimmy.com", + cookie_file="key.conf" + ) + + # 登入 + email = "your@email.com" + password = "your_password" + success = auth.login(email, password) + + if success: + print("✓ 登入成功!") + + # 測試 Cookie + is_valid = auth.test_cookie() + print(f"Cookie 有效: {is_valid}") + else: + print("✗ 登入失敗") + +def env_login_example(): + """使用環境變數登入""" + print("\n=== 使用環境變數登入 ===\n") + + # 從環境變數讀取 + email = os.getenv('MDCLIENT_EMAIL') + password = os.getenv('MDCLIENT_PASSWORD') + url = os.getenv('MDCLIENT_URL', 'https://codimd.lotimmy.com') + + if email and password: + auth = MDAuth(base_url=url) + success = auth.login(email, password) + + if success: + print("✓ 使用環境變數登入成功") + + # 立即可用客戶端 + client = MDClient(base_url=url) + notes = client.list_notes() + print(f"找到 {len(notes)} 筆筆記") + else: + print("✗ 環境變數未設定") + print("請在 .env 檔案中設定:") + print(" MDCLIENT_EMAIL=your@email.com") + print(" MDCLIENT_PASSWORD=your_password") + +def load_existing_session(): + """載入已儲存的會話""" + print("\n=== 載入已儲存的會話 ===\n") + + auth = MDAuth() + + # 檢查是否已有 Cookie + cookies = auth.load_cookie() + if cookies: + print("✓ 找到已儲存的 Cookie") + + # 測試是否仍然有效 + is_valid = auth.test_cookie() + if is_valid: + print("✓ Cookie 仍然有效,可以直接使用") + + # 建立客戶端 + client = MDClient() + notes = client.list_notes() + print(f"找到 {len(notes)} 筆筆記") + else: + print("✗ Cookie 已過期,需要重新登入") + else: + print("✗ 找不到已儲存的 Cookie,請先登入") + +def custom_server_example(): + """連接到自訂伺服器""" + print("\n=== 連接到自訂伺服器 ===\n") + + # 連接到不同的伺服器 + auth = MDAuth( + base_url="https://demo.hedgedoc.org", + cookie_file="hedgedoc_cookies.conf" + ) + + print("提示:取消註解以下程式碼來登入") + # auth.login("user@example.com", "password") + + # client = MDClient( + # base_url="https://demo.hedgedoc.org", + # cookie_file="hedgedoc_cookies.conf" + # ) + +def quick_start(): + """快速開始指南""" + print("=== 快速開始 ===\n") + print("1. 基本使用:") + print(""" +from mdclient import MDAuth, MDClient + +# 登入 +auth = MDAuth() +auth.login("your@email.com", "password") + +# 使用客戶端 +client = MDClient() +notes = client.list_notes() + """) + + print("\n2. 使用 .env 檔案:") + print(""" +# 建立 .env 檔案 +echo "MDCLIENT_EMAIL=your@email.com" > .env +echo "MDCLIENT_PASSWORD=your_password" >> .env + +# 在程式碼中使用 +import os +from dotenv import load_dotenv +from mdclient import MDAuth, MDClient + +load_dotenv() +auth = MDAuth() +auth.login(os.getenv('MDCLIENT_EMAIL'), os.getenv('MDCLIENT_PASSWORD')) + +client = MDClient() +notes = client.list_notes() + """) + + print("\n3. 連接到自訂伺服器:") + print(""" +from mdclient import MDAuth, MDClient + +auth = MDAuth(base_url="https://your-server.com") +auth.login("user@example.com", "password") + +client = MDClient(base_url="https://your-server.com") +notes = client.list_notes() + """) + +def error_handling_example(): + """錯誤處理範例""" + print("\n=== 錯誤處理 ===\n") + + auth = MDAuth() + success = auth.login("wrong@email.com", "wrong_password") + + if not success: + print("✗ 登入失敗,請檢查帳密") + + # 檢查 Cookie 是否存在 + cookies = auth.load_cookie() + if not cookies: + print("✗ 找不到已儲存的會話,請先登入") + else: + print("✓ 找到已儲存的會話") + +if __name__ == "__main__": + quick_start() + # basic_login_example() + # env_login_example() + # load_existing_session() + # custom_server_example() + # error_handling_example() diff --git a/example.py b/example.py new file mode 100644 index 0000000..11c2fc8 --- /dev/null +++ b/example.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +""" +MDclient 使用範例 - Python 函式庫使用方式 + +這個範例展示了如何在 Python 程式碼中使用 MDclient 函式庫 +""" + +from mdclient import MDClient, MDAuth +import os + +def example_basic_usage(): + """基本使用範例""" + print("=== MDclient 基本使用 ===\n") + + # 建立客戶端(會自動讀取目前目錄的 key.conf) + client = MDClient() + + # 列出所有筆記 + print("1. 列出筆記:") + notes = client.list_notes() + if notes: + for i, note in enumerate(notes[:5], 1): + title = note.get('text', '無標題') + note_id = note.get('id', '') + pinned = " 📌" if note.get('pinned') else "" + print(f" {i}. {title}{pinned}") + print(f" ID: {note_id}") + +def example_get_note(): + """取得筆記內容範例""" + print("\n2. 取得筆記內容:") + + client = MDClient() + note_id = "features" # 替換為實際的筆記 ID + note = client.get_note(note_id) + + if note: + content = note.get('content', '') + print(f" 內容預覽: {content[:100]}...") + +def example_create_note(): + """建立筆記範例""" + print("\n3. 建立新筆記:") + + client = MDClient() + result = client.create_note( + title="Python 測試筆記", + content="# 歡迎使用 MDclient\n\n這是一個透過 Python API 建立的筆記。" + ) + + if result: + print(f" 筆記建立成功!") + print(f" ID: {result.get('id')}") + +def example_export_note(): + """匯出筆記範例""" + print("\n4. 匯出筆記:") + + client = MDClient() + note_id = "features" + content = client.export_note(note_id, format="md") + + if content: + filename = f"{note_id}.md" + with open(filename, "w", encoding="utf-8") as f: + f.write(content) + print(f" 筆記已匯出到: {filename}") + +def example_with_env(): + """使用 .env 檔案的範例""" + print("\n=== 使用 .env 設定 ===\n") + + try: + from dotenv import load_dotenv + load_dotenv() + + # 從環境變數讀取設定 + url = os.getenv('MDCLIENT_URL', 'https://codimd.lotimmy.com') + email = os.getenv('MDCLIENT_EMAIL') + password = os.getenv('MDCLIENT_PASSWORD') + + if email and password: + # 登入 + auth = MDAuth(base_url=url) + auth.login(email, password) + + # 使用客戶端 + client = MDClient(base_url=url) + notes = client.list_notes() + print(f"找到 {len(notes)} 筆筆記") + else: + print(" .env 檔案中未設定帳密") + + except ImportError: + print(" 需要安裝 python-dotenv: pip install python-dotenv") + +def example_custom_server(): + """連接到自訂伺服器的範例""" + print("\n=== 連接到自訂伺服器 ===\n") + + # 連接到不同的伺服器 + auth = MDAuth( + base_url="https://your-hedgedoc-server.com", + cookie_file="custom_server_cookies.conf" + ) + + # 注意:需要先登入才能使用客戶端 + # auth.login("user@example.com", "password") + + # client = MDClient( + # base_url="https://your-hedgedoc-server.com", + # cookie_file="custom_server_cookies.conf" + # ) + +def example_search_notes(): + """搜尋筆記範例""" + print("\n5. 搜尋筆記:") + + client = MDClient() + results = client.search_notes("Python") + + if results: + print(f" 找到 {len(results)} 筆相關筆記") + for note in results[:3]: + print(f" - {note.get('text', '無標題')}") + +if __name__ == "__main__": + # 確保已經登入 + if not os.path.exists("key.conf"): + print("請先登入:") + print(" 方式1:使用 .env 檔案") + print(" 方式2:執行認證程式碼") + print("\n範例:") + print(""" +from mdclient import MDAuth +auth = MDAuth() +auth.login("your@email.com", "password") + """) + exit(1) + + # 執行範例 + example_basic_usage() + # example_get_note() + # example_create_note() + # example_export_note() + # example_with_env() + # example_search_notes() + # example_custom_server() diff --git a/mdclient/.gitignore b/mdclient/.gitignore new file mode 100644 index 0000000..d0078d0 --- /dev/null +++ b/mdclient/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +.env +key.conf diff --git a/mdclient/__init__.py b/mdclient/__init__.py new file mode 100644 index 0000000..16f052c --- /dev/null +++ b/mdclient/__init__.py @@ -0,0 +1,11 @@ +""" +MDclient - Markdown 筆記服務的 Python 客戶端函式庫 + +支援 CodiMD、HedgeDoc 等服務的登入、筆記管理等功能 +""" + +from .client import MDClient +from .auth import MDAuth + +__version__ = "0.1.0" +__all__ = ["MDClient", "MDAuth"] diff --git a/mdclient/auth.py b/mdclient/auth.py new file mode 100644 index 0000000..baf2243 --- /dev/null +++ b/mdclient/auth.py @@ -0,0 +1,181 @@ +""" +MDclient 認證模組 +""" + +import requests +import json +from pathlib import Path +from typing import Optional, Dict + + +class MDAuth: + """Markdown 筆記服務認證類""" + + def __init__(self, base_url: str = "https://codimd.lotimmy.com", cookie_file: str = "key.conf"): + """ + 初始化認證類 + + Args: + base_url: 伺服器位址 + cookie_file: Cookie 儲存檔案路徑 + """ + self.base_url = base_url.rstrip("/") + self.login_url = f"{self.base_url}/login" + self.cookie_file = Path(cookie_file) + + def save_cookie(self, cookies: Dict[str, str]) -> Dict[str, str]: + """ + 儲存 Cookie 到檔案 + + Args: + cookies: Cookie 字典 + + Returns: + 儲存的 Cookie 字典 + """ + with open(self.cookie_file, 'w', encoding='utf-8') as f: + json.dump(cookies, f, indent=2) + + print(f"✓ Cookie 已儲存到: {self.cookie_file}") + return cookies + + def load_cookie(self) -> Optional[Dict[str, str]]: + """ + 從檔案載入 Cookie + + Returns: + Cookie 字典,如果檔案不存在則返回 None + """ + if not self.cookie_file.exists(): + return None + + with open(self.cookie_file, 'r', encoding='utf-8') as f: + return json.load(f) + + def login(self, email: str, password: str) -> bool: + """ + 登入並取得 Cookie + + Args: + email: 登入信箱 + password: 登入密碼 + + Returns: + 是否登入成功 + """ + print(f"正在登入 {self.base_url}...") + + session = requests.Session() + + try: + print("1. 取得登入頁面...") + response = session.get(self.login_url, timeout=10) + print(f" 狀態碼: {response.status_code}") + + print("2. 傳送登入請求...") + + login_data = { + 'email': email, + 'password': password, + } + + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Origin': self.base_url, + 'Referer': self.login_url, + } + + # 嘗試不同的登入端點 + login_endpoints = ['/login', '/auth/login', '/api/login', '/signin'] + + for endpoint in login_endpoints: + try: + login_url = f"{self.base_url}{endpoint}" + print(f" 嘗試: {login_url}") + + response = session.post( + login_url, + data=login_data, + headers=headers, + allow_redirects=True, + timeout=10 + ) + + print(f" 狀態碼: {response.status_code}") + print(f" Cookies: {dict(session.cookies)}") + + if response.status_code in [200, 302, 301]: + if session.cookies: + print(f" ✓ 取得到 {len(session.cookies)} 個 Cookie") + + # 轉換為字典 + cookie_dict = {} + for cookie in session.cookies: + cookie_dict[cookie.name] = cookie.value + + # 儲存 cookie + self.save_cookie(cookie_dict) + + # 顯示主要 cookie 資訊 + for name, value in cookie_dict.items(): + if 'sid' in name.lower() or 'session' in name.lower(): + print(f" → {name}: {value[:20]}...") + + return True + + except requests.RequestException as e: + print(f" ✗ 請求失敗: {e}") + continue + + print("✗ 所有登入端點都失敗") + return False + + except requests.RequestException as e: + print(f"✗ 登入失敗: {e}") + return False + + def test_cookie(self) -> bool: + """ + 測試儲存的 Cookie 是否有效 + + Returns: + Cookie 是否有效 + """ + cookies = self.load_cookie() + if not cookies: + print("✗ 找不到儲存的 Cookie") + return False + + print(f"從 {self.cookie_file} 載入 Cookie...") + + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', + } + + try: + response = requests.get( + f"{self.base_url}/", + cookies=cookies, + headers=headers, + timeout=10 + ) + print(f"測試請求狀態碼: {response.status_code}") + return response.status_code == 200 + except requests.RequestException as e: + print(f"✗ 測試失敗: {e}") + return False + + def get_cookies(self) -> Optional[Dict[str, str]]: + """ + 取得 Cookie(如果需要重新登入會自動處理) + + Returns: + Cookie 字典 + """ + cookies = self.load_cookie() + if cookies: + return cookies + + print("找不到有效的 Cookie,請先登入") + return None diff --git a/mdclient/client.py b/mdclient/client.py new file mode 100644 index 0000000..0b3c901 --- /dev/null +++ b/mdclient/client.py @@ -0,0 +1,301 @@ +""" +MDclient 客戶端模組 +""" + +import requests +import json +from pathlib import Path +from typing import Optional, Dict, List, Any + + +class MDClient: + """Markdown 筆記服務客戶端類""" + + def __init__( + self, + base_url: str = "https://codimd.lotimmy.com", + cookie_file: str = "key.conf", + email: Optional[str] = None, + password: Optional[str] = None + ): + """ + 初始化客戶端 + + Args: + base_url: 伺服器位址 + cookie_file: Cookie 儲存檔案路徑 + email: 登入信箱(可選,用於自動登入) + password: 登入密碼(可選,用於自動登入) + """ + self.base_url = base_url.rstrip("/") + self.cookie_file = Path(cookie_file) + self.email = email + self.password = password + + def _load_cookie(self) -> Optional[Dict[str, str]]: + """從檔案載入 Cookie""" + if not self.cookie_file.exists(): + return None + + with open(self.cookie_file, 'r', encoding='utf-8') as f: + return json.load(f) + + def _request( + self, + method: str, + endpoint: str, + data: Optional[Dict] = None, + params: Optional[Dict] = None + ) -> Optional[requests.Response]: + """ + 傳送帶 Cookie 的請求 + + Args: + method: HTTP 方法 (GET, POST, etc.) + endpoint: API 端點 + data: POST 資料 + params: URL 參數 + + Returns: + 回應物件,失敗時返回 None + """ + cookies = self._load_cookie() + + if not cookies: + print(f"✗ 找不到 Cookie 檔案: {self.cookie_file}") + print("請先使用 MDAuth 登入") + return None + + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', + } + + url = f"{self.base_url}{endpoint}" + + try: + if method == 'GET': + response = requests.get(url, cookies=cookies, headers=headers, params=params, timeout=10) + elif method == 'POST': + headers['Content-Type'] = 'application/json' + response = requests.post(url, cookies=cookies, headers=headers, json=data, timeout=10) + elif method == 'DELETE': + response = requests.delete(url, cookies=cookies, headers=headers, timeout=10) + else: + raise ValueError(f"不支援的 HTTP 方法: {method}") + + return response + except requests.RequestException as e: + print(f"✗ 請求失敗: {e}") + return None + + def get_user_info(self) -> Optional[Dict]: + """ + 取得使用者資訊 + + Returns: + 使用者資訊字典 + """ + print("正在取得使用者資訊...") + + endpoints = ['/api/me', '/me', '/api/user/me'] + + for endpoint in endpoints: + response = self._request('GET', endpoint) + if response and response.status_code == 200: + try: + data = response.json() + print("✓ 取得使用者資訊成功") + return data + except json.JSONDecodeError: + pass + + print("✗ 無法取得使用者資訊") + return None + + def list_notes(self) -> Optional[List[Dict]]: + """ + 列出所有筆記 + + Returns: + 筆記列表 + """ + print("正在取得筆記列表...") + + endpoints = ['/api/me/notes', '/api/notes', '/history', '/me/notes'] + + for endpoint in endpoints: + response = self._request('GET', endpoint) + if response and response.status_code == 200: + try: + data = response.json() + print(f"✓ 成功取得筆記 (端點: {endpoint})") + + # 處理不同的回應格式 + if isinstance(data, list): + return data + elif isinstance(data, dict) and 'history' in data: + return data['history'] + elif isinstance(data, dict) and 'notes' in data: + return data['notes'] + + return data + except json.JSONDecodeError: + pass + + print("✗ 無法取得筆記列表") + return None + + def get_note(self, note_id: str) -> Optional[Dict]: + """ + 取得筆記內容 + + Args: + note_id: 筆記 ID + + Returns: + 筆記內容字典 + """ + print(f"正在取得筆記: {note_id}") + + # 嘗試不同的 API 端點 + endpoints = [ + f"/api/notes/{note_id}", + f"/api/notes/{note_id}/content", + f"/{note_id}/download", + f"/{note_id}", + ] + + for endpoint in endpoints: + response = self._request('GET', endpoint) + if response and response.status_code == 200: + try: + # 如果是直接訪問筆記頁面,可能需要解析 HTML + content_type = response.headers.get('content-type', '') + if 'text/html' in content_type: + # 這是 HTML 頁面,不是 API 回應 + continue + + data = response.json() + print(f"✓ 取得筆記成功 (端點: {endpoint})") + return data + except json.JSONDecodeError: + # 如果不是 JSON,可能是純文字內容 + if response.text: + print(f"✓ 取得筆記內容 (端點: {endpoint})") + return { + 'id': note_id, + 'content': response.text, + 'title': note_id + } + + print("✗ 無法取得筆記內容") + return None + + def create_note(self, title: str, content: str = "") -> Optional[Dict]: + """ + 建立新筆記 + + Args: + title: 筆記標題 + content: 筆記內容(Markdown 格式) + + Returns: + 建立的筆記資訊 + """ + print(f"正在建立筆記: {title}") + + data = { + 'title': title, + 'content': content, + } + + response = self._request('POST', '/api/notes', data=data) + + if response and response.status_code in [200, 201]: + try: + result = response.json() + print("✓ 筆記建立成功") + return result + except json.JSONDecodeError: + print("✗ 無法解析回應") + else: + status = response.status_code if response else "無" + print(f"✗ 建立筆記失敗 (狀態碼: {status})") + + return None + + def delete_note(self, note_id: str) -> bool: + """ + 刪除筆記 + + Args: + note_id: 筆記 ID + + Returns: + 是否刪除成功 + """ + print(f"正在刪除筆記: {note_id}") + response = self._request('DELETE', f"/api/notes/{note_id}") + + if response and response.status_code in [200, 204]: + print("✓ 筆記刪除成功") + return True + else: + status = response.status_code if response else "無" + print(f"✗ 刪除筆記失敗 (狀態碼: {status})") + return False + + def export_note(self, note_id: str, format: str = "md") -> Optional[str]: + """ + 匯出筆記 + + Args: + note_id: 筆記 ID + format: 匯出格式 (md, pdf, html, slides) + + Returns: + 匯出的內容 + """ + print(f"正在匯出筆記: {note_id} (格式: {format})") + + endpoints = [ + f"/api/notes/{note_id}/export?format={format}", + f"/{note_id}/download", + f"/{note_id}/export/{format}", + ] + + for endpoint in endpoints: + response = self._request('GET', endpoint) + if response and response.status_code == 200: + print("✓ 筆記匯出成功") + return response.text + + print("✗ 匯出筆記失敗") + return None + + def search_notes(self, query: str) -> Optional[List[Dict]]: + """ + 搜尋筆記 + + Args: + query: 搜尋關鍵字 + + Returns: + 符合的筆記列表 + """ + print(f"正在搜尋筆記: {query}") + + response = self._request('GET', '/api/search', params={'q': query}) + + if response and response.status_code == 200: + try: + data = response.json() + print("✓ 搜尋成功") + return data + except json.JSONDecodeError: + print("✗ 無法解析回應") + else: + status = response.status_code if response else "無" + print(f"✗ 搜尋失敗 (狀態碼: {status})") + + return None diff --git a/mdclient_cli.py b/mdclient_cli.py new file mode 100644 index 0000000..dc8ca37 --- /dev/null +++ b/mdclient_cli.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +""" +MDclient CLI 工具 + +使用 MDclient Python 函式庫的命令列介面 +""" + +import sys +import os +from pathlib import Path + +# 從 mdclient 套件匯入 +from mdclient import MDClient, MDAuth + + +def load_env(): + """載入 .env 檔案""" + env_file = Path(".env") + if env_file.exists(): + with open(env_file, 'r') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#') and '=' in line: + key, value = line.split('=', 1) + os.environ[key.strip()] = value.strip() + + +def main(): + """主函數""" + import argparse + + # 載入 .env 檔案 + load_env() + + parser = argparse.ArgumentParser(description="MDclient - Markdown 筆記服務命令列工具") + parser.add_argument('--url', default=os.getenv('MDCLIENT_URL', 'https://codimd.lotimmy.com'), help='伺服器位址') + parser.add_argument('--cookie-file', default=os.getenv('MDCLIENT_COOKIE_FILE', 'key.conf'), help='Cookie 檔案路徑') + + subparsers = parser.add_subparsers(dest='command', help='可用指令') + + # 登入指令 + login_parser = subparsers.add_parser('login', help='登入並儲存 Cookie') + login_parser.add_argument('email', nargs='?', help='登入信箱(可從 .env 讀取)') + login_parser.add_argument('password', nargs='?', help='登入密碼(可從 .env 讀取)') + + # 測試指令 + subparsers.add_parser('test', help='測試 Cookie 是否有效') + + # 使用者資訊指令 + subparsers.add_parser('info', help='取得使用者資訊') + + # 列出筆記指令 + subparsers.add_parser('list', help='列出所有筆記') + + # 取得筆記指令 + get_parser = subparsers.add_parser('get', help='取得筆記內容') + get_parser.add_argument('note_id', help='筆記 ID') + + # 建立筆記指令 + create_parser = subparsers.add_parser('create', help='建立新筆記') + create_parser.add_argument('title', help='筆記標題') + create_parser.add_argument('--content', '-c', default='', help='筆記內容') + + # 匯出筆記指令 + export_parser = subparsers.add_parser('export', help='匯出筆記') + export_parser.add_argument('note_id', help='筆記 ID') + export_parser.add_argument('--format', '-f', default='md', choices=['md', 'pdf', 'html', 'slides'], help='匯出格式') + export_parser.add_argument('--output', '-o', help='輸出檔案路徑') + + args = parser.parse_args() + + if not args.command: + parser.print_help() + sys.exit(1) + + # 執行指令 + if args.command == 'login': + # 從參數或環境變數取得帳密 + email = args.email or os.getenv('MDCLIENT_EMAIL') + password = args.password or os.getenv('MDCLIENT_PASSWORD') + + if not email or not password: + print("✗ 請提供帳密,或設定 .env 檔案中的 MDCLIENT_EMAIL 和 MDCLIENT_PASSWORD") + sys.exit(1) + + auth = MDAuth(base_url=args.url, cookie_file=args.cookie_file) + success = auth.login(email, password) + if success: + print("\n✓ 登入成功!") + auth.test_cookie() + else: + print("\n✗ 登入失敗") + sys.exit(1) + + elif args.command == 'test': + auth = MDAuth(base_url=args.url, cookie_file=args.cookie_file) + auth.test_cookie() + + elif args.command == 'info': + client = MDClient(base_url=args.url, cookie_file=args.cookie_file) + info = client.get_user_info() + if info: + print("\n使用者資訊:") + print(f" 使用者名稱: {info.get('username', 'N/A')}") + print(f" 電子信箱: {info.get('email', 'N/A')}") + + elif args.command == 'list': + client = MDClient(base_url=args.url, cookie_file=args.cookie_file) + notes = client.list_notes() + if notes: + for i, note in enumerate(notes, 1): + title = note.get('title') or note.get('text') or note.get('alias') or note.get('id', '無標題') + note_id = note.get('id', '') + pinned = " 📌" if note.get('pinned') else "" + print(f"{i}. {title}{pinned} (ID: {note_id})") + + elif args.command == 'get': + client = MDClient(base_url=args.url, cookie_file=args.cookie_file) + note = client.get_note(args.note_id) + if note: + content = note.get('content', '') + print(content) + + elif args.command == 'create': + client = MDClient(base_url=args.url, cookie_file=args.cookie_file) + result = client.create_note(args.title, args.content or "") + if result: + note_id = result.get('id') or result.get('alias', '') + url = result.get('url', f"{args.url}/{note_id}") + print(f"✓ 筆記建立成功!") + print(f" ID: {note_id}") + print(f" 網址: {url}") + + elif args.command == 'export': + client = MDClient(base_url=args.url, cookie_file=args.cookie_file) + content = client.export_note(args.note_id, args.format) + if content and args.output: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(content) + print(f"✓ 已匯出到: {args.output}") + elif content: + print(content) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ff3adc6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,58 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "mdclient" +version = "0.1.0" +description = "Markdown 笔记服务的 Python 客户端库" +readme = "README.md" +license = {text = "MIT"} +authors = [ + {name = "Your Name", email = "your.email@example.com"} +] +keywords = ["markdown", "notes", "codimd", "hedgedoc", "hackmd", "api", "client"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +requires-python = ">=3.8" +dependencies = [ + "requests>=2.28.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "black>=22.0.0", + "flake8>=5.0.0", + "mypy>=0.990", +] +env = [ + "python-dotenv>=1.0.0", +] + +[project.urls] +Homepage = "https://github.com/yourusername/mdclient" +Repository = "https://github.com/yourusername/mdclient" +Issues = "https://github.com/yourusername/mdclient/issues" + +[tool.setuptools.packages.find] +where = ["."] +include = ["mdclient*"] + +[tool.black] +line-length = 100 +target-version = ["py38", "py39", "py310", "py311", "py312"] + +[tool.mypy] +python_version = "3.8" +warn_return_any = true +warn_unused_configs = true