diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt index c642d69..0c280bd 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt @@ -6,6 +6,7 @@ import com.google.gson.annotations.SerializedName import jakarta.servlet.http.HttpServletRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.launch import kr.lunaticbum.back.lun.configs.GlobalEnvironment import kr.lunaticbum.back.lun.model.* @@ -13,11 +14,14 @@ import kr.lunaticbum.back.lun.utils.LogService import org.jsoup.Jsoup import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType import org.springframework.scheduling.annotation.Scheduled import org.springframework.web.bind.annotation.* import org.springframework.web.reactive.function.BodyInserter import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono import java.math.BigDecimal import java.math.RoundingMode import java.net.URLEncoder @@ -54,13 +58,106 @@ class Telegram { @ResponseBody @PostMapping("webhook") - fun test(httpServletRequest: HttpServletRequest, @RequestBody update : kr.lunaticbum.back.lun.model.Result) : String { + suspend fun test(httpServletRequest: HttpServletRequest, @RequestBody update : kr.lunaticbum.back.lun.model.Result) : String { try { println("test strat ${Gson().toJson(update)}") - - println("test strat ${httpServletRequest.requestURI}") +// println("test strat ${httpServletRequest.requestURI}") update?.message?.let { msg -> - if(msg.text?.startsWith("/") == true) { + if(msg?.location != null && msg?.location?.latitude != 0.0 && msg?.location?.latitude != 0.0 ) { + CoroutineScope(Dispatchers.IO).launch { + try { + println("test strat ${msg.location}") + val lat = BigDecimal(msg?.location?.latitude!!).setScale(6, RoundingMode.HALF_UP) + val long = BigDecimal(msg?.location?.longitude!!).setScale(6, RoundingMode.HALF_UP) + WebClient.create().get() + .uri("http://api.weatherapi.com/v1/current.json?key=${globalEvv.weatherApiKey}&q=${lat},${long}&aqi=no") + .retrieve() + .bodyToMono(CurrentWeather::class.java) + .timeout(Duration.ofSeconds(30L)) + .block()?.let { sss -> + println("test strat ${sss}") + CoroutineScope(Dispatchers.IO).launch { + try { + val msg = TelegramSendMsg( + "${msg.from!!.id!!}", + sss.getSummaryInfo(lat.toString(), long.toString()) + ) + println("msg >>> ${Gson().toJson(msg)}") +// val fullUrl = +// "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${msg.userId}&text=${msg.msg}" + val fullUrl = + "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage" + val result = WebClient.create(fullUrl) +// .get() + .post() + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(Gson().toJson(msg))) + .retrieve() + + .bodyToMono(String::class.java).block() ?: "FAIL" + println("fullUrl ${fullUrl} : result $result") + + } catch (e: Exception) { + e.printStackTrace() + } + } + } + var restUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat}%2c${long}&radius=5000&type=restaurant&key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM" + WebClient.create().get() + .uri(restUrl) + .retrieve() + .bodyToMono(PlaceGoogle::class.java) + .timeout(Duration.ofSeconds(30L)) + .block()?.let { sss -> + println("restUrl >>> ${restUrl}") + var topResult = arrayListOf() + sss.results.forEach { + try { + if (it.rating?.toDouble() ?: 0.0 > 4.0) { + it.getDistane(lat.toDouble(),long.toDouble()) + println("${it.name} => ${Gson().toJson(it)}") + topResult.add(it) + } + } catch (e : Exception) { + + } + } + topResult.sortedBy { it.rating }.forEach { + try { + val msg = TelegramSendMsg( + "${msg.from!!.id!!}", + it.toString() + ) + println("msg >>> ${Gson().toJson(msg)}") +// val fullUrl = +// "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${msg.userId}&text=${msg.msg}" + val fullUrl = + "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage" + val result = WebClient.create(fullUrl) +// .get() + .post() + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(Gson().toJson(msg))) + .retrieve() + + .bodyToMono(String::class.java).block() ?: "FAIL" + println("fullUrl ${fullUrl} : result $result") + + } catch (e: Exception) { + e.printStackTrace() + } + } + + } + +// "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${long}&radius=5000&type=cafe&key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM" + } + catch (e : Exception) { + e.printStackTrace() + } + } + + } else if(msg.text?.startsWith("/") == true) { msg.text?.split(" ")?.let { cmds -> cmds[0].let { cmd -> when(cmd.trim()) { @@ -81,6 +178,7 @@ class Telegram { "/lama" -> { val req = BumlamaReq(msg.text!!.replace(cmd,"")) CoroutineScope(Dispatchers.IO).launch { + val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage?chat_id=${globalEvv.telegramMyId}&text=lama 에게 전송 ${req.reqMsg}" logService.log("fullUrl >>> ${fullUrl}") @@ -95,7 +193,7 @@ class Telegram { try { val client = WebClient.create() client.post() - .uri("https://lama.lunaticbum.kr/api/generate") + .uri(lamaGenerated) .body(BodyInserters.fromValue(Gson().toJson(req))) .retrieve() .bodyToMono(String::class.java).timeout(Duration.ofSeconds(6000L)).block()?.let { result -> @@ -166,8 +264,7 @@ class Telegram { } else if (req.reqMsg?.contains("검색") == true) { var originalQuery = req.reqMsg - originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date())) - val gSearch = "https://www.googleapis.com/customsearch/v1?key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM&cx=207f328d3ad7242f2&q=${originalQuery}&num=5&lr=kr" + val gSearch = "https://www.googleapis.com/customsearch/v1?key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM&cx=207f328d3ad7242f2&q=${originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&num=5&lr=kr" println("gSearch >>> ${gSearch}") WebClient.create().get() .uri(gSearch) @@ -198,7 +295,7 @@ class Telegram { val client = WebClient.create() client.post() - .uri("https://lama.lunaticbum.kr/api/generate") + .uri(lamaGenerated) .body(BodyInserters.fromValue(Gson().toJson(req))) .retrieve() .bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { result -> @@ -231,7 +328,7 @@ class Telegram { } else { val client = WebClient.create() client.post() - .uri("https://lama.lunaticbum.kr/api/generate") + .uri(lamaGenerated) .body(BodyInserters.fromValue(Gson().toJson(req))) .retrieve() .bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { result -> @@ -259,34 +356,102 @@ class Telegram { return "Success" } - inner class BumlamaReq { - private constructor() - constructor(reqMsg: String?) { - this.reqMsg = reqMsg + + enum class LamaQueryType(val keywords : ArrayList) { + None(arrayListOf()), + Search(arrayListOf("검색")), + Weather(arrayListOf("날씨")), + NearBy(arrayListOf("주변에","근처에")), + Post(arrayListOf("POST","저장")), + } + + class LamaQuery { + var userQuery : String? = null + var now = SimpleDateFormat("yyyy년MM월dd일 HH:mm:ss").format(Date()) + var userId : String? = null + var queryType : LamaQueryType = LamaQueryType.None + var req : BumlamaReq? = null + var telegramBotKey : String? = null + fun start() { + req = BumlamaReq(userQuery) + LamaQueryType.values().reversed().forEach { type -> + type.keywords.forEach { + if (queryType.equals(LamaQueryType.None)) { + if(userQuery?.contains(it) == true) { + queryType = type + } + } + } + } + when (queryType) { +// LamaQueryType.None -> { +// +// } + LamaQueryType.Search -> { + + } + LamaQueryType.Weather -> { + + } + LamaQueryType.Post -> { + + } + else -> { + askToLama() + } + } + } + + fun searchInfo() { + askToLama() + } + + fun searchWeather() { + askToLama() + } + + fun searchNearBy() { + askToLama() + } + + + + + fun askToLama() { + CoroutineScope(Dispatchers.IO).launch { + req?.let { req -> + val client = WebClient.create() + client.post() + .uri(lamaGenerated) + .body(BodyInserters.fromValue(Gson().toJson(req))) + .retrieve() + .bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { result -> + Gson().fromJson(result, BumlamaResp::class.java)?.let { sss -> + CoroutineScope(Dispatchers.IO).launch { + var toalmsg = "${userQuery}의 대답이 도착했어요.\n" + "${sss.response}" + val fullUrl = "https://api.telegram.org/${telegramBotKey}/sendMessage" + toalmsg.chunked(2048).forEach { chunkedMsg -> + println("fullUrl >>> ${fullUrl}") + var tlgSend = TelegramSendMsg(userId!!, chunkedMsg) + WebClient + .create() + .post() + .uri(fullUrl) + .body(BodyInserters.fromValue(Gson().toJson(tlgSend))) + .retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)) + .block()?.let { result -> + + } + } + } + } + } + } + }.start() } - @SerializedName("prompt") - var reqMsg : String? = "" - var model : String = "phi4:14b" - // var format : String = "json" - var stream = false } - inner class BumlamaResp { - - var model : String? = ""//"phi4:14b", - var created_at : String? = ""// "": "2025-02-13T06:38:53.619359Z", - var response : String? = ""// "{ \n \"response\": \"Hello! How can I assist you today?\" \n}", - var done : Boolean? = true - var done_reason : String? = "stop" - var context : ArrayList? = arrayListOf() - var total_duration : Long = 0L//: 1600246875, - var load_duration : Long = 0L//: 27544792, - var prompt_eval_count : Long = 0L//: 11, - var prompt_eval_duration : Long = 0L//: 279000000, - var eval_count : Long = 0L//: 19, - var eval_duration : Long = 0L//: 1292000000 - } @@ -417,3 +582,42 @@ fun before5Min(): Long { cal.add(Calendar.MINUTE, -10) return cal.timeInMillis } + + +class BumlamaReq { + private constructor() + constructor(reqMsg: String?) { + this.reqMsg = reqMsg + } + + @SerializedName("prompt") + var reqMsg : String? = "" + var model : String = "phi4:14b" + // var format : String = "json" + var stream = false +} + +class BumlamaResp { + + var model : String? = ""//"phi4:14b", + var created_at : String? = ""// "": "2025-02-13T06:38:53.619359Z", + var response : String? = ""// "{ \n \"response\": \"Hello! How can I assist you today?\" \n}", + var done : Boolean? = true + var done_reason : String? = "stop" + var context : ArrayList? = arrayListOf() + var total_duration : Long = 0L//: 1600246875, + var load_duration : Long = 0L//: 27544792, + var prompt_eval_count : Long = 0L//: 11, + var prompt_eval_duration : Long = 0L//: 279000000, + var eval_count : Long = 0L//: 19, + var eval_duration : Long = 0L//: 1292000000 +} + +val lamaGenerated : String = "https://lama.lunaticbum.kr/api/generate" + +data class TelegramSendMsg( + @SerializedName("chat_id") + val userId: String, // null을 허용하지 않음 + @SerializedName("text") + val msg: String // null을 허용하지 않음 +) \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/BumsPrivate.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/BumsPrivate.kt index 82d9c1b..ad6be4e 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/model/BumsPrivate.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/BumsPrivate.kt @@ -75,6 +75,7 @@ interface LocationLogRepository : ReactiveMongoRepository { fun findAllBy() : Mono fun findFirstByOrderByTimeDesc() : Mono + fun findFirstByUserIdOrderByTimeDesc(userId: String) : Mono fun save(log: LocationLog): Mono } interface LocationService { @@ -96,6 +97,10 @@ class LocationLogService : LocationService { return logRepository.findFirstByOrderByTimeDesc().block() } + fun getLocationLogBy(userId : String) : LocationLog? { + return logRepository.findFirstByOrderByTimeDesc().block() + } + fun save(log: LocationLog) { println("saved msg before ${log}") diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/PlaceGoogle.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/PlaceGoogle.kt new file mode 100644 index 0000000..6ee91a5 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/PlaceGoogle.kt @@ -0,0 +1,62 @@ +package kr.lunaticbum.back.lun.model + + +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt + +class PlaceGoogle { + + var results : ArrayList = arrayListOf() +} +class Place { + var name : String = "" + var price_level : String? = null + var rating : String? = null + var reviews : ArrayList? = null + var user_ratings_total : String? = null + var geometry : PlaceGeometry? = null + var distance : Double = 0.0 + var place_id : String? = null + fun getDistane(currentLat : Double, currentLng : Double) : Double { + distance = calculateDistance(currentLat, currentLng, geometry!!.location!!.lat, geometry!!.location!!.lng) + return distance + } + + override fun toString(): String = "상호 : ${name}\n 총 리뷰수: ${user_ratings_total}\n평점 : \n${rating}\n거리 : \n${distance}\n링크 열기: https://www.google.com/maps/search/?api=1&query=${geometry!!.location!!.lat}%2C${geometry!!.location!!.lng}&query_place_id=${place_id}" + +} +class PlaceGeometry { + var location : PlaceLocation? = null +} +class PlaceLocation { + var lat : Double = 0.0//37.3890078, + var lng : Double = 0.0//126.9510394 +} +class PlaceReview { + var author_name : String? = null + var rating : String? = null + var relative_time_description : String? = null + var time : String? = null + var author_url : String? = null + var language : String? = null + var original_language : String? = null + var profile_photo_url : String? = null + var text : String? = null + var translated : String? = null +} + +const val EARTH_RADIUS = 6371.0 // 지구 반지름 (km) + +fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { + val dLat = Math.toRadians(lat2 - lat1) + val dLon = Math.toRadians(lon2 - lon1) + + val a = sin(dLat / 2) * sin(dLat / 2) + + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(dLon / 2) * sin(dLon / 2) + + val c = 2 * atan2(sqrt(a), sqrt(1 - a)) + + return EARTH_RADIUS * c // km 단위 거리 반환 +} \ No newline at end of file diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/TelegramUpdate.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/TelegramUpdate.kt index 77119da..b984ad8 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/model/TelegramUpdate.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/TelegramUpdate.kt @@ -1,5 +1,7 @@ package kr.lunaticbum.back.lun.model +import com.fasterxml.jackson.annotation.JsonProperty +import com.google.gson.annotations.SerializedName import kr.lunaticbum.back.lun.utils.LogService import lombok.AllArgsConstructor import lombok.Data @@ -58,10 +60,32 @@ class Message { var text: String? = null @BsonIgnore var entities: ArrayList? = null - + var location : TelegramLocation? = null // var location : Map? = null } +class TelegramLocation { + @SerializedName("longitude") + var longitude: Double? = null + + @SerializedName("latitude") + var latitude: Double? = null + + @SerializedName("horizontal_accuracy") + var horizontalAccuracy: Double? = null + + + @SerializedName("live_period") + var livePeriod: Int? = null + + + @SerializedName("heading") + var heading: Int? = null + + + @SerializedName("proximity_alert_radius") + var proximityAlertRadius: Int? = null +} class Result { var update_id: Int = 0