From 04ba3c51d02742774e16cfa842f2f2327cfe1f12 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:34:12 +0800 Subject: [PATCH] 1434 --- write-server/scripts/test-artalk-endpoints.sh | 75 +++++++++++++ write-server/src/lib/bot/notify.ts | 103 ++++++++++++++---- 2 files changed, 154 insertions(+), 24 deletions(-) create mode 100644 write-server/scripts/test-artalk-endpoints.sh diff --git a/write-server/scripts/test-artalk-endpoints.sh b/write-server/scripts/test-artalk-endpoints.sh new file mode 100644 index 00000000..d4c355b1 --- /dev/null +++ b/write-server/scripts/test-artalk-endpoints.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# 测试不同的 Artalk API 端点 + +echo "" +echo "===================================" +echo " 测试 Artalk API 端点" +echo "===================================" +echo "" + +# 从 .env 读取配置 +ARTALK_SERVER=$(grep "^ARTALK_SERVER=" .env | cut -d'=' -f2) +ARTALK_SITE=$(grep "^ARTALK_SITE=" .env | cut -d'=' -f2) +ARTALK_ADMIN_EMAIL=$(grep "^ARTALK_ADMIN_EMAIL=" .env | cut -d'=' -f2) +ARTALK_ADMIN_PASSWORD=$(grep "^ARTALK_ADMIN_PASSWORD=" .env | cut -d'=' -f2) + +echo "服务器: $ARTALK_SERVER" +echo "站点: $ARTALK_SITE" +echo "" + +# 1. 登录获取 token +echo "1. 登录获取 token..." +LOGIN_RESPONSE=$(curl -s -X POST "$ARTALK_SERVER/api/v2/auth/email/login" \ + -H "Content-Type: application/json" \ + -d "{\"email\":\"$ARTALK_ADMIN_EMAIL\",\"password\":\"$ARTALK_ADMIN_PASSWORD\"}") + +TOKEN=$(echo "$LOGIN_RESPONSE" | grep -o '"token":"[^"]*"' | cut -d'"' -f4) + +if [ -z "$TOKEN" ]; then + echo "✗ 登录失败" + echo "响应: $LOGIN_RESPONSE" + exit 1 +fi + +echo "✓ 登录成功,获取到 token" +echo "" + +# 2. 测试不同的 API 端点 +echo "2. 测试 API 端点..." +echo "" + +# 端点列表 +ENDPOINTS=( + "/api/v2/pages?site_name=$ARTALK_SITE&limit=100" + "/api/v2/admin/pages?site_name=$ARTALK_SITE&limit=100" + "/api/v2/comments?site_name=$ARTALK_SITE&page_key=/&limit=5" + "/api/v2/comments?site_name=$ARTALK_SITE&limit=5" +) + +for endpoint in "${ENDPOINTS[@]}"; do + echo "测试: $endpoint" + RESPONSE=$(curl -s -w "\n%{http_code}" "$ARTALK_SERVER$endpoint" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $TOKEN") + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" = "200" ]; then + echo "✓ 成功 ($HTTP_CODE)" + echo " 响应: $BODY" | head -c 100 + echo "" + break + else + echo "✗ 失败 ($HTTP_CODE)" + echo " 响应: $BODY" | head -c 100 + echo "" + fi + echo "" +done + +echo "" +echo "===================================" +echo " 测试完成" +echo "===================================" +echo "" diff --git a/write-server/src/lib/bot/notify.ts b/write-server/src/lib/bot/notify.ts index 867fad81..40c1f04a 100644 --- a/write-server/src/lib/bot/notify.ts +++ b/write-server/src/lib/bot/notify.ts @@ -11,6 +11,12 @@ interface ArtalkComment { created_at: string; } +interface ArtalkPage { + id: number; + key: string; + title: string; +} + let lastCheckedId = 0; let notifierStarted = false; let authToken: string | null = null; @@ -67,26 +73,24 @@ async function getAuthToken(): Promise { // 登录获取新 token authToken = await loginToArtalk(); if (authToken) { - // Token 有效期设为 24 小时(Artalk 默认较长) + // Token 有效期设为 24 小时 tokenExpiry = now + 24 * 60 * 60 * 1000; } return authToken; } -// 获取所有评论(使用管理员 API) -async function fetchLatestComments(): Promise { +// 获取所有页面 +async function fetchAllPages(): Promise { try { const token = await getAuthToken(); if (!token) { - console.error("[comment-notify] no auth token available"); + console.error("[comment-notify] no auth token for fetching pages"); return []; } - // 使用管理员 API 获取所有评论 - const url = `${ARTALK_SERVER}/api/v2/admin/comments?site_name=${encodeURIComponent(ARTALK_SITE)}&limit=5&sort_by=date&sort_order=desc`; - console.log("[comment-notify] fetching comments with admin API..."); - console.log("[comment-notify] URL:", url); + const url = `${ARTALK_SERVER}/api/v2/pages?site_name=${encodeURIComponent(ARTALK_SITE)}&limit=200`; + console.log("[comment-notify] fetching pages..."); const res = await fetch(url, { headers: { @@ -95,31 +99,82 @@ async function fetchLatestComments(): Promise { }, }); - console.log("[comment-notify] response status:", res.status, res.statusText); + if (!res.ok) { + console.error("[comment-notify] fetch pages failed:", res.status); + return []; + } + + const data = await res.json() as { pages?: ArtalkPage[]; count?: number }); + console.log("[comment-notify] found", data.pages?.length || 0, "pages (total:", data.count || 0, ")"); + return data.pages || []; + } catch (e) { + console.error("[comment-notify] fetch pages error:", e instanceof Error ? e.message : String(e)); + return []; + } +} + +// 获取特定页面的评论 +async function fetchCommentsForPage(pageKey: string): Promise { + try { + const token = await getAuthToken(); + if (!token) { + return []; + } + + const url = `${ARTALK_SERVER}/api/v2/comments?site_name=${encodeURIComponent(ARTALK_SITE)}&page_key=${encodeURIComponent(pageKey)}&limit=5&sort_by=date&sort_order=desc`; + + const res = await fetch(url, { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}`, + }, + }); if (!res.ok) { - const errorText = await res.text().catch(() => "Unable to read response"); - console.error("[comment-notify] fetch failed:", res.status, res.statusText); - console.error("[comment-notify] response body:", errorText); - - // 如果是认证失败,清除 token 以便下次重试 - if (res.status === 401 || res.status === 403) { - console.error("[comment-notify] authentication failed, clearing token"); - authToken = null; - tokenExpiry = 0; - } - return []; } const data = await res.json() as { comments?: ArtalkComment[] }; - console.log("[comment-notify] fetched", data.comments?.length || 0, "comments"); return data.comments || []; } catch (e) { - console.error("[comment-notify] fetch error:", e instanceof Error ? e.message : String(e)); - if (e instanceof Error && e.stack) { - console.error("[comment-notify] error stack:", e.stack.split('\n').slice(0, 5).join('\n')); + return []; + } +} + +// 获取所有页面的最新评论 +async function fetchLatestComments(): Promise { + try { + // 1. 获取所有页面 + const pages = await fetchAllPages(); + if (pages.length === 0) { + console.log("[comment-notify] no pages found"); + return []; } + + console.log("[comment-notify] checking comments from", pages.length, "pages..."); + + // 2. 并行获取每个页面的评论(只查最新的 20 个页面,避免过多请求) + const recentPages = pages.slice(0, 20); + const promises = recentPages.map(async (page) => { + const comments = await fetchCommentsForPage(page.key); + return comments; + }); + + const results = await Promise.all(promises); + const allComments: ArtalkComment[] = []; + results.forEach(comments => allComments.push(...comments)); + + // 3. 按时间排序,取最新的 5 条 + allComments.sort((a, b) => + new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + ); + + const latestComments = allComments.slice(0, 5); + console.log("[comment-notify] fetched", latestComments.length, "latest comments from", recentPages.length, "recent pages"); + + return latestComments; + } catch (e) { + console.error("[comment-notify] fetch error:", e instanceof Error ? e.message : String(e)); return []; } }