package com.pomelotea.hoperun.sign.notify import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONObject import com.pomelotea.hoperun.sign.api.model.DingTalkMarkdownRequest import com.pomelotea.hoperun.sign.api.model.DingTalkNotifyRequest import com.pomelotea.hoperun.sign.api.model.DingTalkNotifyResponse import com.pomelotea.hoperun.sign.api.model.MarkdownContent import com.pomelotea.hoperun.sign.common.client import com.pomelotea.hoperun.sign.config.DingTalkConfig import com.pomelotea.hoperun.sign.scheduler.AutoDakaScheduler.Companion.logger import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import org.springframework.stereotype.Service import java.net.URLEncoder import java.nio.charset.StandardCharsets import java.util.Base64 import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec import kotlin.experimental.and @Service open class DingTalkNotifyHelper( private val dingTalkConfig: DingTalkConfig ) { fun sendText(content: String): DingTalkNotifyResponse { if (!dingTalkConfig.enabled) { logger.info("钉钉通知未启用,跳过发送消息") return DingTalkNotifyResponse(0, "钉钉通知未启用") } val request = DingTalkNotifyRequest( text = com.pomelotea.hoperun.sign.api.model.TextContent(content) ) return sendRequest(request) } fun sendMarkdown(title: String, content: String): DingTalkNotifyResponse { if (!dingTalkConfig.enabled) { logger.info("钉钉通知未启用,跳过发送消息") return DingTalkNotifyResponse(0, "钉钉通知未启用") } val request = DingTalkMarkdownRequest( markdown = MarkdownContent(title, content) ) return sendRequest(request) } private fun sendRequest(request: Any): DingTalkNotifyResponse { try { val url = generateSignedUrl() val jsonBody = JSON.toJSONString(request) logger.info("发送钉钉消息: $jsonBody") val httpRequest = Request.Builder() .url(url) .post(jsonBody.toRequestBody("application/json;charset=utf-8".toMediaTypeOrNull())) .build() val response = client.newCall(httpRequest).execute() val responseBody = response.body?.string() logger.info("钉钉响应: $responseBody") return JSONObject.parseObject(responseBody, DingTalkNotifyResponse::class.java) } catch (e: Exception) { logger.error("发送钉钉消息失败", e) return DingTalkNotifyResponse(-1, "发送钉钉消息失败: ${e.message}") } } private fun generateSignedUrl(): String { val webhook = dingTalkConfig.webhook val secret = dingTalkConfig.secret if (secret.isEmpty()) { return webhook } val timestamp = System.currentTimeMillis() val sign = generateSign(timestamp.toString(), secret) return "$webhook×tamp=$timestamp&sign=$sign" } private fun generateSign(timestamp: String, secret: String): String { val stringToSign = "$timestamp\n$secret" val mac = Mac.getInstance("HmacSHA256") mac.init(SecretKeySpec(secret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256")) val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8)) val sign = Base64.getEncoder().encodeToString(signData) return URLEncoder.encode(sign, StandardCharsets.UTF_8.name()) } }