This commit is contained in:
lunaticbum 2025-02-27 17:08:15 +09:00
parent deb4a8e262
commit 7c4d5cde69
6 changed files with 422 additions and 206 deletions

View File

@ -53,6 +53,11 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
compileOnly("org.projectlombok:lombok")
// implementation(platform("com.google.cloud:libraries-bom: 26.55.0"))
// implementation("com.google.cloud:google-cloud-apikeys")
implementation ("com.google.maps:google-maps-services:2.2.0")
implementation ("org.slf4j:slf4j-simple:1.7.25")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
implementation("io.jsonwebtoken:jjwt-impl:0.11.5")
implementation("io.jsonwebtoken:jjwt-jackson:0.11.5")

View File

@ -4,6 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.web.reactive.function.client.WebClient
import java.io.IOException
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@EnableScheduling // 추가
@ -20,6 +24,8 @@ class LunApplication {
}
}
@ -30,4 +36,3 @@ fun main(args: Array<String>) {
}
runApplication<LunApplication>(*args)
}

View File

@ -1,12 +1,18 @@
package kr.lunaticbum.back.lun.controllers
import bums.lunatic.launcher.utils.CompressStringUtil
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import com.google.maps.GeoApiContext
import com.google.maps.PendingResult
import com.google.maps.PlacesApi
import com.google.maps.model.LatLng
import com.google.maps.model.PlaceType
import com.google.maps.model.PlacesSearchResult
import com.google.maps.model.RankBy
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.*
@ -14,22 +20,18 @@ 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.ui.ModelMap
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
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.Duration
import java.util.*
import kotlin.collections.ArrayList
import java.util.prefs.Preferences
@RestController
@ -56,16 +58,82 @@ class Telegram {
@Autowired
lateinit var rssDataService: RssDataService
@ResponseBody
@GetMapping("kesy/{path}")
fun getEncode(@PathVariable path: String): ModelMap {
var returnModelMap = ModelMap()
var comp = CompressStringUtil.compressString(path)
println("comp >>> $comp")
val keyworkd = arrayListOf("I0Z","dcBEW", "TGyG", "U=Qu", "Bm=s")
val keyworkd2 = arrayListOf("x-n", "Y_D", "u", "uoo", "dfhZ", "gSKY")
var chunked = Math.abs(Random().nextInt() % 3) + 1
chunked = if (chunked % 2 == 1) chunked + 1 else chunked
comp = comp?.chunked(chunked) {
return@chunked it.padStart(chunked,'=')
}?.joinToString("")?.reversed().plus("$chunked").plus(Char(Math.abs(Random().nextInt() % 57) + 65))
println("comp >>> $comp")
var word = if (System.currentTimeMillis() % 2L == 0L) {
keyworkd.get(chunked)
} else {
comp = comp.plus(Char(Math.abs(Random().nextInt() % 57) + 65))
keyworkd2.get(chunked)
}
comp = (word).plus(comp)
returnModelMap.put("C",comp)
var doubleIpmt = false
keyworkd2.forEach { if(comp?.startsWith(it) == true) {
doubleIpmt = true
} }
var charChunked = comp?.lastOrNull()
println("comp?.removeSuffix(charChunked!!.toString()) ${comp?.removeSuffix(charChunked!!.toString())}")
comp = comp?.removeSuffix(charChunked!!.toString())
if (doubleIpmt) {
charChunked = comp?.lastOrNull()
comp = comp?.removeSuffix(charChunked!!.toString())
}
charChunked = comp?.lastOrNull()
println("charChunked >> $charChunked")
chunked = charChunked?.toString()?.toInt() ?: 0
println("chunked >> $chunked")
println("comp?.removeSuffix(charChunked!!.toString()) ${comp?.removeSuffix(charChunked!!.toString())}")
comp = (comp?.substring(0,comp.length -1))
println("comp $comp")
comp = comp?.removePrefix(keyworkd.get(chunked))?.removePrefix(keyworkd2.get(chunked))?.reversed()
println("comp $comp")
comp = comp?.chunked(chunked){
return@chunked it.toString().replace("=","")
}?.joinToString("")
println("comp $comp")
var decomp = CompressStringUtil.decompressString(comp)
println("decomp $decomp")
returnModelMap.put("D", decomp)
return returnModelMap
}
@ResponseBody
@PostMapping("webhook")
suspend 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?, @RequestBody updates : kr.lunaticbum.back.lun.model.TelegramUpdate? ) : String {
try {
println("test strat ${Gson().toJson(updates)}")
println("test strat ${Gson().toJson(update)}")
// println("test strat ${httpServletRequest.requestURI}")
update?.message?.let { msg ->
if(msg?.location != null && msg?.location?.latitude != 0.0 && msg?.location?.latitude != 0.0 ) {
CoroutineScope(Dispatchers.IO).launch {
try {
var prefKey = Preferences.userNodeForPackage(Telegram::class.java).get("GAPI_KEY".plus("_").plus(msg.from!!.id.toString()), "")
if (prefKey != null && prefKey.length > 0) {
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)
@ -82,13 +150,9 @@ class Telegram {
"${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)))
@ -102,74 +166,71 @@ class Telegram {
}
}
}
var restUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat}%2c${long}&radius=2000&type=restaurant&key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM&rankby=prominence"
WebClient.create().get()
.uri(restUrl)
.retrieve()
.bodyToMono(PlaceGoogle::class.java)
.timeout(Duration.ofSeconds(30L))
.block()?.let { sss ->
println("restUrl >>> ${restUrl}")
val context = GeoApiContext.Builder()
.apiKey(prefKey)
.build()
var types =
arrayOf(PlaceType.RESTAURANT, PlaceType.CAFE, PlaceType.BAR, PlaceType.BAKERY)
types.forEach { type ->
PlacesApi.nearbySearchQuery(context, LatLng(lat.toDouble(), long.toDouble()))
.type(type).rankby(RankBy.DISTANCE).language("ko").await()?.let { respoce ->
respoce.results.filter {
return@filter it.rating > 4 && it.userRatingsTotal > 1
}.sortedBy { it.userRatingsTotal }.forEach {
try {
var topResult = arrayListOf<Place>()
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) {
e.printStackTrace()
}
}
topResult.forEach {
try {
val msg = TelegramSendMsg("${msg.from!!.id!!}", it.toString())
val msg = TelegramSendMsg(
"${msg.from!!.id!!}",
"${type.name} :: " + it.summary(lat.toDouble(), long.toDouble())
)
println("msg >>> ${Gson().toJson(msg)}")
val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
val fullUrl =
"https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
val result = WebClient.create(fullUrl)
.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()
}
}
} 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"
}
else {
sendSimpleMsg(globalEvv.telegramBotKey!!,msg.from!!.id.toString(),"서비스 키를 등록하셈.\n/setGaipKeys {key}")
}
}
catch(e : Exception) {
e.printStackTrace()
}
}
} else if(msg.text?.startsWith("/") == true) {
msg.text?.split(" ")?.let { cmds ->
cmds[0].let { cmd ->
when(cmd.trim()) {
"/reqGapiKeys" -> {
sendSimpleMsg(globalEvv.telegramBotKey!!,globalEvv.telegramMyId!!,"${msg.from!!.id.toString()}님이 서비스 키를 요첨항./setGaipKeys {key}")
}
"/setGaipKeys" -> {
Preferences.userNodeForPackage(Telegram::class.java).put("GAPI_KEY".plus("_").plus(msg.from!!.id.toString()), cmds[1])
}
"/get" ->{}
"/jf" ->{
CoroutineScope(Dispatchers.IO).launch {
logService.log("${cmd} Start ${cmds[1]}")
String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL3NlYXJjaC9tb3ZpZS8lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService) }
logService.log("${cmd} END ${cmds[1]}")
}
CoroutineScope(Dispatchers.IO).launch {
logService.log("on Cmd JF with SO")
logService.log("${cmd} Start ${cmds[1]}")
String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9rcjcwLnNvZ2lybC5zby8/cz0lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService)}
logService.log("${cmd} END ${cmds[1]}")
}
// CoroutineScope(Dispatchers.IO).launch {
// logService.log("${cmd} Start ${cmds[1]}")
// String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9qYXZtb3N0LnRvL3NlYXJjaC9tb3ZpZS8lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService) }
// logService.log("${cmd} END ${cmds[1]}")
// }
// CoroutineScope(Dispatchers.IO).launch {
// logService.log("on Cmd JF with SO")
// logService.log("${cmd} Start ${cmds[1]}")
// String.format(String(Base64.getMimeDecoder().decode("aHR0cHM6Ly9rcjcwLnNvZ2lybC5zby8/cz0lcw==".toByteArray())),cmds[1]).getJ().let { doc -> FeedParseManager.parse(doc,rssDataService)}
// logService.log("${cmd} END ${cmds[1]}")
// }
}
"/lama" -> {
val req = BumlamaReq(msg.text!!.replace(cmd,""))
@ -448,6 +509,20 @@ class Telegram {
}
fun sendSimpleMsg(telegramBotKey : String , userId : String, msg :String) {
val fullUrl = "https://api.telegram.org/${telegramBotKey}/sendMessage"
var tlgSend = TelegramSendMsg(userId, msg)
WebClient
.create()
.post()
.uri(fullUrl)
.body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
.retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
.block()?.let { result ->
}
}
@ -493,69 +568,6 @@ class Telegram {
}
// @Bean
// @Scheduled(cron = "0 0/2 * * * *") //
// fun pollingTelegramUpdate() {
// try {
// logService.log("pollingTelegramUpdate telegramBotKey >>>> ${globalEvv.telegramBotKey}")
// logService.log("pollingTelegramUpdate telegramMyId >>>> ${globalEvv.telegramMyId}")
// logService.log("pollingTelegramUpdate weatherApiKey >>>> ${globalEvv.weatherApiKey}")
// if (
// ((globalEvv.weatherApiKey?.length ?: 0) > 3 )&&
// ((globalEvv.telegramBotKey?.length ?: 0) > 3 )&&
// ((globalEvv.telegramMyId?.length ?: 0) > 3)
// ) {
// val client0 = WebClient.create()
// val result = client0.get()
// .uri("https://api.telegram.org/${globalEvv.telegramBotKey}/getUpdates")
// .retrieve()
// .bodyToMono(String::class.java).block() ?: "FAIL"
// logService.log("pollingTelegramUpdate result >>>> $result")
// Gson().fromJson(result, TelegramUpdate::class.java)?.let { sss ->
// logService.log("pollingTelegramUpdate sss >>>> $sss")
// if (sss.isSucces()) {
// sss.result?.filter {
// ((it.message?.date ?: 0L) * 1000L) > before5Min()
// }?.forEach {
// logService.log("pollingTelegramUpdate before Query doOnSuccess m >>>> ${it}")
// it.message?.let { msg ->
// logService.log("pollingTelegramUpdate before Query doOnSuccess m >>>> ${msg.message_id}")
// qns(msg.message_id,msg)
// }
// }
// }
// }
// }
// }catch (e : Exception) {
// e.printStackTrace()
// }
// }
// fun qns(it : String, msg : Message) {
// var doSave = true
// telegramService.findById(it)?.subscribe( { m ->
// logService.log("pollingTelegramUpdate doOnSuccess m >>>> $m")
// if (m != null) {
// if (msg.text?.contains("어디") == true) {
//
// } else {
// logService.log(msg.text ?: "NONE")
// }
// } else {
// doSave = false
// }
// },{ e ->
// e.printStackTrace()
// },{
// if (doSave) {
// telegramService.save(msg)
// if (msg.text?.contains("어디") == true || msg.text?.startsWith("\"") == true) {
// sendMsg()
// }
// }
// logService.log("pollingTelegramUpdate doOnSuccess comp")
// })
// }
fun sendMsg(target : String) {
val client = WebClient.create()
@ -617,3 +629,7 @@ data class TelegramSendMsg(
@SerializedName("text")
val msg: String // null을 허용하지 않음
)
fun PlacesSearchResult.summary(currentLat : Double,currentLng: Double) : String {
return "${name}\n총 리뷰수: ${userRatingsTotal}\n평점 : ${rating}\n거리 : \n${calculateDistance(currentLat, currentLng, geometry!!.location!!.lat, geometry!!.location!!.lng)}km\n링크:\n https://www.google.com/maps/search/?api=1&query=${geometry!!.location!!.lat}%2C${geometry!!.location!!.lng}&query_place_id=${placeId}"
}

View File

@ -6,46 +6,55 @@ import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
class PlaceGoogle {
var results : ArrayList<Place> = arrayListOf()
}
class Place {
var name : String = ""
var price_level : String? = null
var rating : String? = null
var reviews : ArrayList<PlaceReview>? = 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
}
//
//class PlaceGoogle {
//
// var results : ArrayList<Place> = arrayListOf()
//}
//class Place {
// var name : String? = ""
//
// var price_level : String? = null
//
// var rating : String? = null
//
//// var reviews : ArrayList<PlaceReview>? = 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 ?: 0.0
// }
//
// 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)
@ -60,3 +69,85 @@ fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): D
return EARTH_RADIUS * c // km 단위 거리 반환
}
// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString, Root.class); */
class GPGeometry {
var location: GPLocation? = null
var viewport: GPViewport? = null
}
class GPLocation {
var lat: Double = 0.0
var lng: Double = 0.0
}
class GPNortheast {
var lat: Double = 0.0
var lng: Double = 0.0
}
class GPOpeningHours {
var open_now: Boolean = false
}
class GPPhoto {
var height: Int = 0
var html_attributions: ArrayList<String>? = null
var photo_reference: String? = null
var width: Int = 0
}
class GPPlusCode {
var compound_code: String? = null
var global_code: String? = null
}
class GPResult {
var business_status: String? = null
var geometry: GPGeometry? = null
var icon: String? = null
var icon_background_color: String? = null
var icon_mask_base_uri: String? = null
var name: String? = null
var opening_hours: GPOpeningHours? = null
var photos: ArrayList<GPPhoto>? = null
var place_id: String? = null
var plus_code: GPPlusCode? = null
var price_level: Int = 0
var rating: Double = 0.0
var reference: String? = null
var scope: String? = null
var types: ArrayList<String>? = null
var user_ratings_total: Int = 0
var vicinity: String? = null
var permanently_closed: Boolean = false
var distance : Double = 0.0
fun getDistane(currentLat : Double, currentLng : Double) : Double {
distance = calculateDistance(currentLat, currentLng, geometry!!.location!!.lat, geometry!!.location!!.lng)
return distance ?: 0.0
}
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 GPlace {
var error_message : String? = null
var html_attributions: ArrayList<Any>? = null
var next_page_token: String? = null
var results: ArrayList<GPResult>? = null
var status: String? = null
}
class GPSouthwest {
var lat: Double = 0.0
var lng: Double = 0.0
}
class GPViewport {
var northeast: GPNortheast? = null
var southwest: GPSouthwest? = null
}

View File

@ -22,7 +22,7 @@ import reactor.core.publisher.Mono
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString, Root.class); */
class Chat {
var id: Int = 0
var id: Long = 0
var first_name: String? = null
var last_name: String? = null
var username: String? = null
@ -36,7 +36,7 @@ class Entity {
}
class From {
var id: Int = 0
var id: Long = 0
var is_bot: Boolean = false
var first_name: String? = null
var last_name: String? = null

View File

@ -0,0 +1,99 @@
package bums.lunatic.launcher.utils
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.io.UnsupportedEncodingException
import java.util.zip.DeflaterOutputStream
import java.util.zip.InflaterInputStream
import kotlin.math.floor
object CompressStringUtil {
val charsetName: String = "UTF-8"
/**
* String 객체를 압축하여 String 으로 리턴한다.
* @param string
* @return
*/
@Synchronized
fun compressString(string: String): String? {
return byteToString(compress(string))
}
/**
* 압축된 스트링을 복귀시켜서 String 으로 리턴한다.
* @param compressed
* @return
*/
@Synchronized
fun decompressString(compressed: String?): String {
return decompress(hexToByteArray(compressed))
}
private fun byteToString(bytes: ByteArray?): String? {
val sb = StringBuilder()
try {
for (b in bytes!!) {
sb.append(String.format("%02X", b))
}
} catch (e: Exception) {
e.printStackTrace()
return null
}
return sb.toString()
}
private fun compress(text: String): ByteArray? {
val baos = ByteArrayOutputStream()
try {
val out: OutputStream = DeflaterOutputStream(baos)
out.write(text.toByteArray(charset(charsetName)))
out.close()
} catch (e: UnsupportedEncodingException) {
e.printStackTrace()
return null
} catch (e: IOException) {
e.printStackTrace()
return null
}
return baos.toByteArray()
}
private fun decompress(bytes: ByteArray): String {
val `in`: InputStream = InflaterInputStream(ByteArrayInputStream(bytes))
val baos = ByteArrayOutputStream()
try {
val buffer = ByteArray(8192)
var len: Int
while ((`in`.read(buffer).also { len = it }) > 0) baos.write(buffer, 0, len)
return String(baos.toByteArray(), charset(charsetName))
} catch (e: IOException) {
e.printStackTrace()
throw AssertionError(e)
}
}
/**
* 16 문자열을 byte 배열로 변환한다.
*/
private fun hexToByteArray(hex: String?): ByteArray {
if (hex == null || hex.length % 2 != 0) {
return byteArrayOf()
}
val bytes = ByteArray(hex.length / 2)
var i = 0
while (i < hex.length) {
val value = hex.substring(i, i + 2).toInt(16).toByte()
bytes[floor((i / 2).toDouble()).toInt()] = value
i += 2
}
return bytes
}
}