nodebb-python/nodebb_sdk.py

149 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
from typing import Dict, Any
from loguru import logger
class NodeBBAPIError(Exception):
"""自定义 NodeBB API 错误异常"""
def __init__(self, message: str, status_code: int = None, response_text: str = None):
super().__init__(message)
self.status_code = status_code
self.response_text = response_text
def __str__(self):
if self.status_code:
return f"{self.status_code}: {super().__str__()} | Response: {self.response_text}"
return super().__str__()
class NodeBBClient:
"""
一个用于与 NodeBB v3 API 交互的客户端 SDK。
"""
def __init__(self, base_url: str, api_token: str):
"""
初始化 NodeBB 客户端。
:param base_url: 你的 NodeBB 论坛 URL (例如: "https://forum.example.com")
:param api_token: 你在 NodeBB 后台生成的 API Bearer Token
"""
if not base_url or not api_token:
raise ValueError("base_url 和 api_token 不能为空")
# 确保 URL 末尾没有斜杠,方便拼接
self.base_url = base_url.rstrip('/')
self._headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
def _post(self, endpoint: str, payload: Dict[str, Any]) -> Dict[str, Any]:
"""
一个内部辅助方法,用于发送 POST 请求。
:param endpoint: API 的端点 (例如: "/api/v3/topics")
:param payload: 要发送的 JSON 数据
:return: API 响应的 JSON 字典
:raises NodeBBAPIError: 如果 API 请求失败
"""
api_url = f"{self.base_url}{endpoint}"
try:
response = requests.post(api_url, headers=self._headers, json=payload, timeout=15)
# 检查 HTTP 状态码,如果不是 2xx则抛出异常
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
# 捕获 HTTP 错误,并用我们的自定义异常包装它
raise NodeBBAPIError(
message=f"API 请求返回了一个错误",
status_code=e.response.status_code,
response_text=e.response.text
) from e
except requests.exceptions.RequestException as e:
# 捕获其他请求相关的错误 (如网络问题)
raise NodeBBAPIError(f"请求失败: {e}") from e
def create_topic(self, cid: int, title: str, content: str) -> Dict[str, Any]:
"""
在指定板块创建一个新的主题 (发帖)。
:param cid: 板块 ID (Category ID)
:param title: 主题标题
:param content: 主题内容 (支持 Markdown)
:return: 成功时返回 API 响应的 JSON 数据
:raises NodeBBAPIError: 如果 API 请求失败
"""
logger.info(f"SDK: 正在向板块 (cid={cid}) 发布新主题: '{title}'...")
payload = {
"cid": cid,
"title": title,
"content": content
}
response_data = self._post("/api/v3/topics", payload)
logger.success("✅ SDK: 主题发布成功!")
logger.info(f" - 主题 ID (tid): {response_data['response']['tid']}")
logger.info(f" - 访问链接: {self.base_url}/topic/{response_data['response']['slug']}")
return response_data['response']
def reply_to_topic(self, tid: int, content: str) -> Dict[str, Any]:
"""
回复一个已存在的主题 (跟帖)。
:param tid: 主题 ID (Topic ID)
:param content: 回复内容 (支持 Markdown)
:return: 成功时返回 API 响应的 JSON 数据
:raises NodeBBAPIError: 如果 API 请求失败
"""
logger.info(f"SDK: 正在回复主题 (tid={tid})...")
endpoint = f"/api/v3/topics/{tid}"
payload = {
"content": content
}
response_data = self._post(endpoint, payload)
logger.success("✅ SDK: 回复成功!")
logger.info(f" - 新帖子 ID (pid): {response_data['response']['pid']}")
logger.info(f" - 访问链接: {self.base_url}/post/{response_data['response']['pid']}")
return response_data['response']
# --- 以下是如何使用这个 SDK 的示例 ---
if __name__ == "__main__":
# --- 配置区 ---
NODEBB_FORUM_URL = "https://your-nodebb-forum.com" # 你的 NodeBB 论坛 URL
NODEBB_API_TOKEN = "your_api_bearer_token_here" # 你的 API Bearer Token
# --- 配置区结束 ---
# 1. 检查配置是否已填写
if "your-nodebb-forum.com" in NODEBB_FORUM_URL or "your_api_bearer_token_here" in NODEBB_API_TOKEN:
logger.error("❌ 请先在脚本中修改 'NODEBB_FORUM_URL''NODEBB_API_TOKEN' 的值。")
else:
# 2. 创建 NodeBBClient 实例
try:
client = NodeBBClient(base_url=NODEBB_FORUM_URL, api_token=NODEBB_API_TOKEN)
# --- 使用示例:发帖并立即回复 ---
# 3. 定义帖子内容
category_id = 2 # 目标板块ID
topic_title = "来自 NodeBB SDK 的测试"
topic_content = "这是一个使用 `NodeBBClient` 类发送的主题。代码结构更清晰了!"
# 4. 使用 try...except 来捕获潜在的 API 错误
try:
# 5. 调用 create_topic 方法
new_topic = client.create_topic(cid=category_id, title=topic_title, content=topic_content)
# 6. 从成功的响应中获取新主题的ID
new_topic_id = new_topic['tid']
logger.success(f"\n新主题已创建 (tid={new_topic_id}),现在对其进行回复...")
# 7. 调用 reply_to_topic 方法
client.reply_to_topic(tid=new_topic_id, content="这是来自 SDK 的自动沙发回复!🎉")
except NodeBBAPIError as e:
logger.error("\n--- 操作失败 ---")
logger.error(f"❌ 发生 API 错误: {e}")
except ValueError as e:
logger.error(f"❌ 初始化客户端失败: {e}")