atrade/src/main/kotlin/network/NewsService.kt
2026-04-07 17:32:21 +09:00

126 lines
4.8 KiB
Kotlin

package network
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.engine.cio.CIOEngineConfig
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.parameter
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpHeaders
import io.ktor.http.Parameters
import io.ktor.http.Url
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import model.DartFinancialResponse
import model.KisSession
import model.NaverNewsResponse
import service.SafeScraper
import service.UrlCacheManager
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.Locale
import kotlin.Double
object NewsService {
private val client = HttpClient<CIOEngineConfig>(CIO) {
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true })
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.BODY
}
}
suspend fun fetchAndIngestNews(corpInfo: CorpInfo) {
val clientId = KisSession.config.nAppKey // 설정에서 가져오도록 수정 필요
val clientSecret = KisSession.config.nSecretKey
val qlistNews = listOf(
"${corpInfo.stockName} 주가",
"${corpInfo.stockName} 실적",
"${corpInfo.stockName} 공시",
// "${corpInfo.stockName} 이벤트"
)
val qlistCorpTrend = listOf(
"${corpInfo.cName} 최근 동향",
"${corpInfo.cName} 이슈",
// "${corpInfo.cName} 투자",
// "${corpInfo.cName} 실적"
)
(qlistNews + qlistCorpTrend).forEach { query ->
try {
val formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH)
val today = ZonedDateTime.now().toLocalDate() // 오늘 날짜 정보
val response: NaverNewsResponse = client.get("https://openapi.naver.com/v1/search/news.json") {
parameter("query", query)
parameter("display", 4) // 최근 10개 뉴스
parameter("sort", "date") // 유사도 순 (또는 date 발간순)
header("X-Naver-Client-Id", clientId)
header("X-Naver-Client-Secret", clientSecret)
}.body()
val todayItems = response.items.filter { item ->
try {
val pubDate = ZonedDateTime.parse(item.pubDate, formatter)
pubDate.toLocalDate() == today // 날짜가 오늘과 일치하는지 확인
} catch (e: Exception) {
false
}
}
// 중복 호스트 제거 및 최종 2건 선택
val finalItems = todayItems
.distinctBy { Url(it.originallink).host }
.take(2)
if (finalItems.isNotEmpty()) {
SafeScraper.scrapeParallel(corpInfo, finalItems)
}
} catch (e: Exception) {
println("❌ 뉴스 가져오기 실패: ${e.message}")
}
}
}
suspend fun fetchFinancialGrowth(corpCode: String?): String {
if (corpCode != null) {
val apiKey = KisSession.config.dAppKey
// 단일회사 주요계정 API (재무상태표, 손익계산서 주요 항목)
val url = "https://opendart.fss.or.kr/api/fnlttSinglAcnt.json?crtfc_key=$apiKey&corp_code=$corpCode&bsns_year=2024&reprt_code=11011"
return try {
val response = client.get(url).body<DartFinancialResponse>()
val accounts = response.list ?: return "재무 데이터 없음"
var buffer : StringBuffer = StringBuffer()
buffer.append("[재무 분석 데이터]").append("\n")
response.list.forEach { it
buffer.append("${it.account_nm} (당기)${it.thstrm_amount} (전기)${it.frmtrm_amount}").append("\n")
}
return buffer.toString()
} catch (e: Exception) {
"재무 API 연동 실패: ${e.message}"
}
} else {
return ""
}
}
}