This commit is contained in:
lunaticbum 2026-05-06 15:53:55 +09:00
parent 0413fa3e2e
commit d7efc433bd
7 changed files with 107 additions and 4 deletions

View File

@ -328,6 +328,7 @@ fun main() = application {
// DashboardScreen()
}
AppScreen.TradingDecision -> {
TradingDecisionLog()
}
}

View File

@ -1,10 +1,16 @@
import androidx.compose.runtime.mutableStateListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import model.AppConfig
import model.TradingDecision
import network.NewsService
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.datetime
import org.jetbrains.exposed.sql.transactions.experimental.suspendedTransactionAsync
import org.jetbrains.exposed.sql.transactions.transaction
import report.TradingReportManager
import report.TradingReportService
@ -509,11 +515,21 @@ object TradingLogStore {
decisionLogs.add(
LogEntry(
time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
stockName = "${tradingDecision.stockName}[${tradingDecision.currentPrice}][]",
stockName = "${tradingDecision.stockName}[${tradingDecision.currentPrice}]",
decision = decision,
confidence = tradingDecision.confidence,
reason = log
)
).apply {
CoroutineScope(Dispatchers.Default).launch {
println("CALLED sendTelegramMessage -1")
if (decision.contains("WATCH") || ((tradingDecision.investmentGrade?.ordinal
?: 0) < 2)
) {
println("CALLED sendTelegramMessage OK")
NewsService.sendTelegramMessage("${this@apply.decision} ${tradingDecision.stockName}[${tradingDecision.currentPrice}] ${log}")
}
}
}
)
}
}
@ -545,8 +561,14 @@ object TradingLogStore {
decision = "NOTICE",
confidence = 100.0,
reason = log
)
).apply {
CoroutineScope(Dispatchers.Default).launch {
println("CALLED sendTelegramMessage")
NewsService.sendTelegramMessage("${this@apply.decision}$name[$code] ${log}")
}
}
)
}
}

View File

@ -240,6 +240,7 @@ class TradeConfig {
var start_buy_time : String = "08:55"
var end_buy_time : String = "15:10"
var enableOverSea : Boolean = false
var tlg_id : String = ""
}

View File

@ -45,7 +45,7 @@ object KisTradeService {
// [수정] 모든 로그(Headers + Body)를 찍도록 설정
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.NONE
level = LogLevel.ALL
}
}

View File

@ -17,10 +17,14 @@ 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
import io.ktor.http.ContentType.Application.Json
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.http.Url
import io.ktor.http.contentType
import io.ktor.network.tls.TLSConfigBuilder
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@ -29,10 +33,16 @@ import model.KisSession
import model.NaverNewsResponse
import service.SafeScraper
import service.UrlCacheManager
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.URL
import java.net.URLEncoder
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.Locale
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import kotlin.Double
object NewsService {
@ -122,4 +132,62 @@ object NewsService {
return ""
}
}
suspend fun sendTelegramMessage(data: String) {
Thread {
try {
var chatId = KisSession.tradeConfig.tlg_id
println("sendTelegramMessage $chatId")
sendViaSystemCurl("https://lunaticbum.kr/tlg/sendToMe.bjx",chatId,data)
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
fun sendViaSystemCurl(url : String, chatId: String, message: String) {
try {
// 메시지 내 공백이나 한글이 깨지지 않도록 인코딩 (필수)
val encodedMessage = URLEncoder.encode(message, "UTF-8")
// OS 확인
val isWindows = System.getProperty("os.name").lowercase().contains("win")
val command = if (isWindows) {
// 윈도우용: 큰따옴표 이스케이프에 주의해야 합니다.
val jsonBody = "{\"id\":\"$chatId\",\"message\":\"$encodedMessage\"}"
listOf("cmd", "/c", "curl -s -X POST $url -H \"Content-Type: application/json\" -d \"$jsonBody\"")
} else {
// 맥/리눅스용: 홑따옴표를 사용하여 JSON 구조를 보호합니다.
val jsonBody = "{\"id\":\"$chatId\",\"message\":\"$message\"}"
listOf("curl", "-s", "-X", "POST", url, "-H", "Content-Type: application/json", "-d", jsonBody)
}
val process = ProcessBuilder(command)
.redirectErrorStream(true) // 에러 출력(stderr)을 표준 출력(stdout)으로 합침
.start()
// 프로세스의 출력을 읽어오는 블록
BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
val output = StringBuilder()
var line: String?
while (reader.readLine().also { line = it } != null) {
output.append(line).append("\n")
}
val exitCode = process.waitFor() // 프로세스가 종료될 때까지 대기
println("--- Telegram Curl Log Start ---")
println("Exit Code: $exitCode") // 0이면 성공, 그 외는 curl 에러 코드
println("Response:\n$output")
println("--- Telegram Curl Log End ---")
}
} catch (e: Exception) {
println("시스템 명령어 실행 중 예외 발생: ${e.message}")
e.printStackTrace()
}
}
}

View File

@ -45,6 +45,7 @@ import java.awt.Toolkit
import java.awt.datatransfer.DataFlavor
import java.io.File
import androidx.compose.ui.input.key.*
import network.NewsService
fun getPastedPathFromClipboard(): String? {
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
@ -138,6 +139,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) {
SystemSleepPreventer.wakeDisplay() // 모니터 켜기
statusMessage = "⏰ 자동 실행 시간(08:30)입니다. 시스템을 가동합니다."
authenticateAndStart()
break // 성공하면 루프 탈출
}
}

View File

@ -42,10 +42,13 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import model.ConfigIndex
import model.KisSession
import network.KisTradeService
import network.NewsService
import network.StockUniverseLoader
import service.AutoTradingManager
import java.io.File
@ -62,7 +65,13 @@ fun TradingDecisionLog() {
var llmAnalyser by remember { mutableStateOf(AutoTradingManager.llmAnalyser) }
val tradeConfig by remember {
KisSession.tradeConfig = KisSession.loadTradeConfig()
CoroutineScope(Dispatchers.Default).launch {
println("CALLED sendTelegramMessage -1")
val now = java.time.LocalTime.now(java.time.ZoneId.of("Asia/Seoul"))
NewsService.sendTelegramMessage("⏰ 자동 실행 시간(${now.hour}:${now.minute})입니다. 시스템을 가동합니다.")
}
mutableStateOf(KisSession.tradeConfig)
}
LaunchedEffect(AutoTradingManager.llmAnalyser) {
llmAnalyser = AutoTradingManager.llmAnalyser