Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 103 additions & 49 deletions src/main/kotlin/org/hydev/back/controller/CommentController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import com.github.kotlintelegrambot.entities.ChatId
import com.github.kotlintelegrambot.entities.InlineKeyboardMarkup
import com.github.kotlintelegrambot.entities.ParseMode
import com.github.kotlintelegrambot.entities.keyboard.InlineKeyboardButton
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.annotation.PreDestroy
import org.hydev.back.*
import org.hydev.back.ai.HarmLevel
import org.hydev.back.ai.IHarmClassifier
Expand All @@ -24,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.util.HtmlUtils
import java.sql.Date
import java.util.concurrent.ConcurrentHashMap
import javax.servlet.http.HttpServletRequest


Expand Down Expand Up @@ -56,6 +63,49 @@ class CommentController(
return "✅ 已为评论 #$commentId 添加备注:\n$noteContent"
}

private val controllerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

@PreDestroy
fun onDestroy() {
controllerScope.cancel("CommentController 正在关闭,取消所有未完成的审核任务")
}

private val processingCommentIds = ConcurrentHashMap.newKeySet<Long>()

/**
Comment thread
Misaka13514 marked this conversation as resolved.
* Acquire the in-process lock for [id], then launch an IO coroutine to run [block].
* - Lock taken → answer callback with a busy alert and return immediately.
* - Lock acquired → silently acknowledge the callback (stops spinner), run [block] on IO dispatcher.
* Errors inside [block] are caught, logged, and reported to the admin chat.
* The lock is always released in `finally`.
*/
private fun withCommentLock(
bot: com.github.kotlintelegrambot.Bot,
id: Long,
callbackQueryId: String,
chatId: ChatId,
actionName: String,
block: suspend CoroutineScope.() -> Unit
) {
if (!processingCommentIds.add(id)) {
bot.answerCallbackQuery(callbackQueryId, text = "⚠️ 有人正在处理这条评论,请稍候", showAlert = true)
return
}
bot.answerCallbackQuery(callbackQueryId)
controllerScope.launch {
try {
Comment thread
Misaka13514 marked this conversation as resolved.
block()
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Comment thread
Misaka13514 marked this conversation as resolved.
System.err.println("[!] Failed to $actionName comment $id: $e")
bot.sendMessage(chatId, "[!] #$id ${actionName}失败: $e")
} finally {
Comment thread
Misaka13514 marked this conversation as resolved.
processingCommentIds.remove(id)
}
}
}

private val actionButtons = mapOf(
"通过" to "pass",
"Spoiler 后通过" to "pass-spoiler",
Expand All @@ -82,80 +132,82 @@ class CommentController(
}

if (data == "comment-cancel") {
bot.editMessageReplyMarkup(chatId, msgId, inlId, replyMarkup)
withCommentLock(bot, id, callbackQuery.id, chatId, "cancel") {
bot.editMessageReplyMarkup(chatId, msgId, inlId, replyMarkup)
}
return@callback
}

if (data.startsWith("comment-confirm-")) {
val action = data.removePrefix("comment-confirm-")
bot.editMessageReplyMarkup(chatId, msgId, inlId, null)

when (action) {
// Ban ip
"ban" -> {
"ban" -> withCommentLock(bot, id, callbackQuery.id, chatId, "ban") {
val comment = commentRepo.queryById(id)!!
val ip = comment.ip
val entry = Ban(ip = ip, reason = "Bad comment #$id")
banRepo.save(entry)

val escapedPersonId = HtmlUtils.htmlEscape(comment.personId)
val escapedContent = HtmlUtils.htmlEscape(comment.content)
val escapedIp = HtmlUtils.htmlEscape(ip)
val escapedOperator = HtmlUtils.htmlEscape(operator)
banRepo.save(Ban(ip = ip, reason = "Bad comment #$id"))
val banMessage = """
#$id - $escapedPersonId 收到了新的留言:
#$id - ${HtmlUtils.htmlEscape(comment.personId)} 收到了新的留言:

<blockquote expandable><tg-spoiler>$escapedContent</tg-spoiler></blockquote>
<blockquote expandable><tg-spoiler>${HtmlUtils.htmlEscape(comment.content)}</tg-spoiler></blockquote>

- 已封禁🚫 $escapedIp by $escapedOperator
- 已封禁🚫 ${HtmlUtils.htmlEscape(ip)} by ${HtmlUtils.htmlEscape(operator)}
""".trimIndent()

println("[-] Comment banned! IP $ip banned by $operator due to Comment $id")
bot.editMessageText(chatId, msgId, inlId, banMessage, parseMode = ParseMode.HTML)
}

// Rejected, remove
"reject" -> {
"reject" -> withCommentLock(bot, id, callbackQuery.id, chatId, "reject") {
println("[-] Comment rejected! Comment $id deleted by $operator")
bot.editMessageText(chatId, msgId, inlId, "$message\n- 已删除❌ by $operator")
}

// Commit changes
"pass", "pass-spoiler" -> {
"pass", "pass-spoiler" -> withCommentLock(bot, id, callbackQuery.id, chatId, "pass") {
Comment thread
Misaka13514 marked this conversation as resolved.
var statusMsgId = 0L
bot.sendMessage(chatId, "正在提交更改...").fold(
{ statusMsgId = it.messageId },
{ System.err.println("> Failed to send submission message: $it") })
val comment = commentRepo.queryById(id)!!

// Spoiler
if (action == "pass-spoiler")
comment.content = "||${comment.content.replace('\n', ' ')}||"

// Create commit content
val fPath = "people/${comment.personId}/comments/${date("yyyy-MM-dd")}-C${comment.id}.json"
val cMsg = "[+] Comment added by ${comment.submitter} for ${comment.personId}"

// Build JSON content with optional replies
val content = json("id" to comment.id, "content" to comment.content,
"submitter" to comment.submitter, "date" to comment.date,
*comment.note?.let { arrayOf("replies" to listOf(mapOf("content" to it, "submitter" to "Maintainer"))) } ?: arrayOf())
println("[+] Comment approved. Adding Comment $id: $content by $operator")

// Write commit
val url = commitDirectly(comment.submitter, DataEdit(fPath, content), cMsg)
bot.deleteMessage(chatId, statusMsgId)

// Update database
comment.approved = true
commentRepo.save(comment)

// Attach URL
bot.editMessageText(chatId, msgId, inlId, "$message\n- 已通过审核✅ by $operator", replyMarkup =
InlineKeyboardMarkup.createSingleRowKeyboard(
InlineKeyboardButton.Url(text = "查看 Commit", url = url)
try {
val comment = commentRepo.queryById(id)!!
if (comment.approved) {
// Already processed (e.g. approved before this server restarted), notify and bail
bot.sendMessage(chatId, "⚠️ 这条评论 (#$id) 已经被处理过了")
Comment thread
Misaka13514 marked this conversation as resolved.
return@withCommentLock
}
bot.sendMessage(chatId, "正在提交更改...").fold(
{ statusMsgId = it.messageId },
{ System.err.println("> Failed to send submission message: $it") })

// Spoiler
if (action == "pass-spoiler")
comment.content = "||${comment.content.replace('\n', ' ')}||"

// Create commit content
val fPath = "people/${comment.personId}/comments/${date("yyyy-MM-dd")}-C${comment.id}.json"
val cMsg = "[+] Comment added by ${comment.submitter} for ${comment.personId}"

// Build JSON content with optional replies
val content = json("id" to comment.id, "content" to comment.content,
"submitter" to comment.submitter, "date" to comment.date,
*comment.note?.let { arrayOf("replies" to listOf(mapOf("content" to it, "submitter" to "Maintainer"))) } ?: arrayOf())
println("[+] Comment approved. Adding Comment $id: $content by $operator")

// Write commit
val url = commitDirectly(comment.submitter, DataEdit(fPath, content), cMsg)

// Update database
comment.approved = true
commentRepo.save(comment)

// Attach URL
bot.editMessageText(chatId, msgId, inlId, "$message\n- 已通过审核✅ by $operator", replyMarkup =
InlineKeyboardMarkup.createSingleRowKeyboard(
InlineKeyboardButton.Url(text = "查看 Commit", url = url)
)
)
)
} finally {
if (statusMsgId != 0L) bot.deleteMessage(chatId, statusMsgId)
}
}
}
return@callback
Expand All @@ -170,7 +222,9 @@ class CommentController(
)
} ?: replyMarkup

bot.editMessageReplyMarkup(chatId, msgId, inlId, confirmMarkup)
withCommentLock(bot, id, callbackQuery.id, chatId, "confirm-markup") {
bot.editMessageReplyMarkup(chatId, msgId, inlId, confirmMarkup)
}
}

@PostMapping("/add")
Expand Down
Loading