Skip to content

Commit

Permalink
feat: discord 알림 서비스
Browse files Browse the repository at this point in the history
  • Loading branch information
DongGeon0908 committed Aug 18, 2024
1 parent 938ae95 commit b5dd130
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
oauth.kakao.rest-api-key: ${{ secrets.OAUTH_KAKAO_REST_API_KEY }}
oauth.kakao.client-secret-code: ${{ secrets.OAUTH_KAKAO_CLIENT_SECRET_CODE }}
oauth.kakao.admin-key: ${{ secrets.OAUTH_KAKAO_ADMIN_KEY }}
# Discord-Webhook-client 환경 변수 주입
client.discord.webhook.credential.1.webhookId: ${{ secrets.CLIENT_DISCORD_WEBHOOK_CREDENTIAL_1_WEBHOOK_ID }}
client.discord.webhook.credential.1.webhookToken: ${{ secrets.CLIENT_DISCORD_WEBHOOK_CREDENTIAL_1_WEBHOOK_TOKEN }}

# Secret Setup - application-prod.yml
- name: Inject env-values to application-prod.yml
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.hero.alignlab.client.discord.client

import com.hero.alignlab.client.discord.model.request.SendMessageRequest
import com.hero.alignlab.client.discord.model.response.GetWebhookWithTokenResponse

interface DiscordWebhookClient {
suspend fun getWebhookWithToken(id: Int): GetWebhookWithTokenResponse

suspend fun sendMessage(id: Int, request: SendMessageRequest): Void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.hero.alignlab.client.discord.client

import com.hero.alignlab.client.discord.config.DiscordWebhookClientConfig
import com.hero.alignlab.client.discord.model.request.SendMessageRequest
import com.hero.alignlab.client.discord.model.response.GetWebhookWithTokenResponse
import com.hero.alignlab.client.kakao.SuspendableClient
import org.springframework.web.reactive.function.client.WebClient

class SuspendableDiscordWebhookClient(
client: WebClient,
private val config: DiscordWebhookClientConfig.Config,
) : DiscordWebhookClient, SuspendableClient(client) {
override suspend fun getWebhookWithToken(id: Int): GetWebhookWithTokenResponse {
return client
.get()
.discordUri(id)
.request()
}

override suspend fun sendMessage(id: Int, request: SendMessageRequest): Void {
return client
.post()
.discordUri(id)
.bodyValue(request)
.request()
}

private fun WebClient.RequestHeadersUriSpec<*>.discordUri(id: Int): WebClient.RequestHeadersSpec<*> {
val credential = getWebhookCredential(id)
return this.uri("/${credential.webhookId}/${credential.webhookToken}")
}

private fun WebClient.RequestBodyUriSpec.discordUri(id: Int): WebClient.RequestBodySpec {
val credential = getWebhookCredential(id)
return this.uri("/${credential.webhookId}/${credential.webhookToken}")
}

private fun getWebhookCredential(id: Int): DiscordWebhookClientConfig.Config.WebhookContext {
return config.credential[id] ?: throw RuntimeException("Webhook Credential does not exist, $id")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.hero.alignlab.client.discord.config

import com.hero.alignlab.client.WebClientFactory
import com.hero.alignlab.client.discord.client.DiscordWebhookClient
import com.hero.alignlab.client.discord.client.SuspendableDiscordWebhookClient
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.validation.annotation.Validated

@Validated
@Configuration
class DiscordWebhookClientConfig {
private val logger = KotlinLogging.logger { }

@Bean
@ConditionalOnProperty(prefix = "client.discord.webhook", name = ["url"])
@ConfigurationProperties(prefix = "client.discord.webhook")
fun discordWebhookConfig() = Config()

@Bean
@ConditionalOnBean(name = ["discordWebhookConfig"])
@ConditionalOnMissingBean(DiscordWebhookClient::class)
fun discordWebhookClient(
@Valid discordWebhookConfig: Config
): DiscordWebhookClient {
logger.info { "initialized DiscordWebhookClient. $discordWebhookConfig" }

val webclient = WebClientFactory.generate(discordWebhookConfig.url)

return SuspendableDiscordWebhookClient(webclient, discordWebhookConfig)
}

data class Config(
@field:NotBlank
var url: String = "",
var credential: Map<Int, WebhookContext> = emptyMap()
) {
data class WebhookContext(
@field:NotBlank
var webhookId: String = "",
@field:NotBlank
var webhookToken: String = "",
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.hero.alignlab.client.discord.model.request

data class SendMessageRequest(
val content: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.hero.alignlab.client.discord.model.response

import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.annotation.JsonNaming

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class GetWebhookWithTokenResponse(
val applicationId: String? = null,
val avatar: String? = null,
val channelId: String? = null,
val guildId: String? = null,
val id: String? = null,
val name: String? = null,
val type: Int? = null,
val token: String? = null,
val url: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.hero.alignlab.domain.dev.resource

import com.hero.alignlab.client.discord.client.DiscordWebhookClient
import com.hero.alignlab.client.discord.model.request.SendMessageRequest
import com.hero.alignlab.config.dev.DevResourceCheckConfig.Companion.devResource
import com.hero.alignlab.config.swagger.SwaggerTag.DEV_TAG
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.*

@Tag(name = DEV_TAG)
@RestController
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
class DevDiscordWebhookResource(
private val discordWebhookClient: DiscordWebhookClient,
) {
@Operation(summary = "discord webhook test")
@PostMapping("/api/dev/v1/discord-webhooks/{id}")
suspend fun sendMessage(
@PathVariable id: Int,
@RequestHeader("X-HERO-DEV-TOKEN") token: String,
@RequestParam message: String,
) = devResource(token) {
discordWebhookClient.sendMessage(id, SendMessageRequest(message))
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.hero.alignlab.domain.health.resource

import com.hero.alignlab.domain.health.model.response.HealthResponse
import com.hero.alignlab.common.extension.wrapOk
import com.hero.alignlab.domain.health.model.response.HealthResponse
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.core.env.Environment
import org.springframework.http.MediaType
Expand All @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
class HealthResource(
private val environment: Environment
private val environment: Environment,
) {
@GetMapping("/api/v1/health")
suspend fun healthCheckV1() = environment.activeProfiles.first()
Expand Down
7 changes: 7 additions & 0 deletions src/main/resources/config/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ client:
kakao-info:
url: https://kapi.kakao.com
unlink-path: /v1/user/unlink
discord:
webhook:
url: https://discord.com/api/webhooks
credential:
1:
webhookId:
webhookToken:

encrypt:
key:
Expand Down

0 comments on commit b5dd130

Please sign in to comment.