diff --git a/.gitignore b/.gitignore index 1b20f46..59379dd 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ lerna-debug.log* *.ctxt ### Package Files +*.jar *.war *.nar *.ear @@ -70,6 +71,12 @@ lerna-debug.log* *.tar.gz *.rar +### UI build output +ui/dist/ +ui/dist-ssr/ +ui/*.local +ui/.eslintcache + ### Local file application-local.yml application-local.yaml diff --git a/build.gradle b/build.gradle index 6af1ed1..4d6cd49 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'top.nxxy335.commentaiautopilot' -version '1.0.0' +version '1.0.4' repositories { mavenCentral() diff --git a/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java b/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java index 4910205..135a523 100644 --- a/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java +++ b/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java @@ -291,26 +291,51 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint { var commentTime = String.valueOf(comment.getMetadata().getCreationTimestamp()); var isCommentAi = isAiOwner(comment.getSpec().getOwner()); + // 首条评论没有引用对象 var commentMsg = new ConversationMessage( - "comment", commentOwner, commentContent, commentTime, isCommentAi + "comment", commentOwner, commentContent, commentTime, isCommentAi, null, null ); return client.list(Reply.class, reply -> commentName.equals(reply.getSpec().getCommentName()), null) .sort(Comparator.comparing(r -> r.getMetadata().getCreationTimestamp())) - .map(reply -> { - var replyOwner = extractOwnerName(reply.getSpec().getOwner()); - var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent()); - var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp()); - var isAi = isAiOwner(reply.getSpec().getOwner()); - return new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi); - }) - .collectList() + .collectList() // 收集为List以便统一处理引用映射 .map(replyList -> { List messages = new ArrayList<>(); messages.add(commentMsg); - messages.addAll(replyList); + + // 构建 Reply 的映射字典,方便查找引用关系 + Map replyMap = new HashMap<>(); + for (Reply r : replyList) { + replyMap.put(r.getMetadata().getName(), r); + } + + for (Reply reply : replyList) { + var replyOwner = extractOwnerName(reply.getSpec().getOwner()); + var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent()); + var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp()); + var isAi = isAiOwner(reply.getSpec().getOwner()); + + String quoteOwner = null; + String quoteContent = null; + + // 获取引用的 Reply 名称 (Halo中如果为空,代表直接回复顶级 Comment) + String quoteReplyName = reply.getSpec().getQuoteReply(); + if (quoteReplyName != null && !quoteReplyName.isBlank()) { + Reply quotedReply = replyMap.get(quoteReplyName); + if (quotedReply != null) { + quoteOwner = extractOwnerName(quotedReply.getSpec().getOwner()); + quoteContent = extractContent(quotedReply.getSpec().getRaw(), quotedReply.getSpec().getContent()); + } + } else { + // 没有 quoteReply 表示直接回复首条评论 + quoteOwner = commentOwner; + quoteContent = commentContent; + } + + messages.add(new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi, quoteOwner, quoteContent)); + } return messages; }); }) @@ -712,7 +737,9 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint { String owner, String content, String time, - boolean isAi + boolean isAi, + String quoteOwner, + String quoteContent ) {} public record CommenterInfo( diff --git a/src/main/java/top/nxxy335/commentaiautopilot/service/CommentReplyPublisher.java b/src/main/java/top/nxxy335/commentaiautopilot/service/CommentReplyPublisher.java index 82428d9..4c56b17 100644 --- a/src/main/java/top/nxxy335/commentaiautopilot/service/CommentReplyPublisher.java +++ b/src/main/java/top/nxxy335/commentaiautopilot/service/CommentReplyPublisher.java @@ -80,63 +80,63 @@ public class CommentReplyPublisher { private Mono doPublish(String parentCommentName, String replyContent, String postName, String quoteReplyName, boolean autoPublish, String personaName) { - return resolvePersona(personaName).flatMap(persona -> { - String displayName = persona.displayName(); - String email = persona.email(); - Reply reply = new Reply(); - reply.setMetadata(new Metadata()); - reply.getMetadata().setName(generateReplyName()); - reply.setSpec(new Reply.ReplySpec()); + // 解析 AI 角色并直接发布纯净的回复内容 + return resolvePersona(personaName) + .flatMap(persona -> { + String displayName = persona.displayName(); + String email = persona.email(); - var spec = reply.getSpec(); - spec.setCommentName(parentCommentName); - spec.setRaw(replyContent); - spec.setContent(replyContent); - spec.setApproved(autoPublish); - if (autoPublish) { - spec.setApprovedTime(Instant.now()); - } - spec.setPriority(0); - spec.setTop(false); - spec.setAllowNotification(false); - spec.setHidden(false); + Reply reply = new Reply(); + reply.setMetadata(new Metadata()); + reply.getMetadata().setName(generateReplyName()); + reply.setSpec(new Reply.ReplySpec()); - if (quoteReplyName != null && !quoteReplyName.isBlank()) { - spec.setQuoteReply(quoteReplyName); - } + var spec = reply.getSpec(); + spec.setCommentName(parentCommentName); - var owner = new Comment.CommentOwner(); - owner.setKind(Comment.CommentOwner.KIND_EMAIL); - if (email != null && !email.isBlank()) { - owner.setName(email); - } else { - owner.setName(AI_PERSONA_OWNER_PREFIX + displayName); - } - owner.setDisplayName(displayName + " AI"); + // 直接存入纯净的 AI 回复内容,不加任何 Markdown 前缀 + spec.setRaw(replyContent); + spec.setContent(replyContent); - Map ownerAnnotations = new HashMap<>(); - ownerAnnotations.put("comment-ai-autopilot.nxxy335.top/is-ai", "true"); - // 使用Gravatar邮箱头像 - if (email != null && !email.isBlank()) { - String gravatarUrl = GravatarUtil.generateUrl(email); - ownerAnnotations.put(Comment.CommentOwner.AVATAR_ANNO, gravatarUrl); - } - owner.setAnnotations(ownerAnnotations); - spec.setOwner(owner); + spec.setApproved(autoPublish); + if (autoPublish) { + spec.setApprovedTime(Instant.now()); + } + spec.setPriority(0); + spec.setTop(false); + spec.setAllowNotification(false); + spec.setHidden(false); - log.info("[Publisher] Creating reply for comment: {}, owner: kind={}, name={}, displayName={}, annotations={}", - parentCommentName, owner.getKind(), owner.getName(), owner.getDisplayName(), ownerAnnotations); + // Halo 原生评论组件正是靠这个字段来渲染 "回复 @某人" 的 + if (quoteReplyName != null && !quoteReplyName.isBlank()) { + spec.setQuoteReply(quoteReplyName); + } - return client.create(reply) - .doOnSuccess(created -> { - var createdOwner = created.getSpec().getOwner(); - log.info("[Publisher] AI Persona '{}' reply published for comment: {}, quoteReply: {}, owner annotations after create: {}", - displayName, parentCommentName, quoteReplyName, - createdOwner != null ? createdOwner.getAnnotations() : "null"); - }) - .doOnError(e -> log.error("[Publisher] Failed to publish AI reply: {}", e.getMessage())); - }); + var owner = new Comment.CommentOwner(); + owner.setKind(Comment.CommentOwner.KIND_EMAIL); + if (email != null && !email.isBlank()) { + owner.setName(email); + } else { + owner.setName(AI_PERSONA_OWNER_PREFIX + displayName); + } + owner.setDisplayName(displayName + " AI"); + + Map ownerAnnotations = new HashMap<>(); + ownerAnnotations.put("comment-ai-autopilot.nxxy335.top/is-ai", "true"); + if (email != null && !email.isBlank()) { + String gravatarUrl = GravatarUtil.generateUrl(email); + ownerAnnotations.put(Comment.CommentOwner.AVATAR_ANNO, gravatarUrl); + } + owner.setAnnotations(ownerAnnotations); + spec.setOwner(owner); + + log.info("[Publisher] Creating reply for comment: {}, content length: {}", parentCommentName, replyContent.length()); + + return client.create(reply) + .doOnSuccess(created -> log.info("[Publisher] AI Persona '{}' reply published for comment: {}", displayName, parentCommentName)) + .doOnError(e -> log.error("[Publisher] Failed to publish AI reply: {}", e.getMessage())); + }); } /** diff --git a/src/main/resources/plugin.yaml b/src/main/resources/plugin.yaml index cea8919..d2c7f1d 100644 --- a/src/main/resources/plugin.yaml +++ b/src/main/resources/plugin.yaml @@ -30,4 +30,4 @@ spec: url: "https://github.com/sunny-335/plugin-comment-ai-autopilot/blob/main/LICENSE" settingName: "comment-ai-autopilot-settings" configMapName: "comment-ai-autopilot-configmap" - version: "1.0.0" + version: "1.0.4" diff --git a/ui/src/views/LogsView.vue b/ui/src/views/LogsView.vue index ff337eb..5c33315 100644 --- a/ui/src/views/LogsView.vue +++ b/ui/src/views/LogsView.vue @@ -1,59 +1,33 @@