100 lines
3.5 KiB
Kotlin
100 lines
3.5 KiB
Kotlin
|
|
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())
|
||
|
|
}
|
||
|
|
}
|