Added ktor site.
This commit is contained in:
parent
8d90282588
commit
bdcac0f494
6
.idea/kotlinc.xml
generated
Normal file
6
.idea/kotlinc.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="1.9.20" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -1,5 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
|
kotlin("jvm") version "1.9.20"
|
||||||
|
kotlin("plugin.serialization") version "1.9.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "org.example"
|
group = "org.example"
|
||||||
@ -12,8 +14,20 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(platform("org.junit:junit-bom:5.9.1"))
|
testImplementation(platform("org.junit:junit-bom:5.9.1"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||||
|
implementation("io.ktor:ktor-server-core-jvm:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-server-cio-jvm:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-server-status-pages-jvm:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-server-default-headers-jvm:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-client-core:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-client-cio:2.3.5")
|
||||||
|
implementation("io.ktor:ktor-server-html-builder:2.3.5")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(11)
|
||||||
|
}
|
||||||
@ -1,2 +1,5 @@
|
|||||||
|
plugins {
|
||||||
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
||||||
|
}
|
||||||
rootProject.name = "VeloDecode"
|
rootProject.name = "VeloDecode"
|
||||||
|
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.Security;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
private static final byte[] key = "BatCaveGGevaCtaB".getBytes(Charset.defaultCharset());
|
|
||||||
public static String decodeVeloBs(String input) {
|
|
||||||
try {
|
|
||||||
var encoded = Base64.getDecoder().decode(input);
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(key, "AES"));
|
|
||||||
return new String(cipher.doFinal(encoded));
|
|
||||||
}catch (Exception ignored){}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String encodeVeloBs(String input){
|
|
||||||
try {
|
|
||||||
var encoded = input.getBytes();
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE,new SecretKeySpec(key, "AES"));
|
|
||||||
return new String(Base64.getEncoder().encode(cipher.doFinal(encoded)));
|
|
||||||
}catch (Exception ignored){}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args){
|
|
||||||
Security.setProperty("crypto.policy", "unlimited");
|
|
||||||
|
|
||||||
//URL url = new URL("http://www.velocidrone.com/api/leaderboard/getLeaderBoard");
|
|
||||||
//HttpURLConnection veloConnection = (HttpURLConnection) url.openConnection();
|
|
||||||
//veloConnection.setDoInput(true);
|
|
||||||
//veloConnection.setDoOutput(true);
|
|
||||||
//veloConnection.connect();
|
|
||||||
//var writer = new OutputStreamWriter(veloConnection.getOutputStream());
|
|
||||||
//writer.write("post_data=wQb/V5e6u70LZd5g1C1ZulicB4/KTXGAErDvWW0VdpuxrJf87bxEqN4dq55t2m5t530teJLczZM31JJLVwDMzKx9jvmxQNZ/Vo1abXxWamEpvmRRF1fHw2rOFK98VpWP");
|
|
||||||
//var reader = new BufferedReader(new InputStreamReader(veloConnection.getInputStream()));
|
|
||||||
|
|
||||||
|
|
||||||
//var baseString = "0afWszJKVVHqYD1vYdg8+z8E0/Jg7QSa/J+OMhUi4TY6+CoMRHLVpH88cg0DnRGaVtn54YlVzChaTgkFVXPKzeAMK8z7AIzg8ZpVPoAJGH1pHFaHM/6ZHX1W+Uakv1MW+FztGI+hgszogU4DcQFWNstBgOD33yBxJlhjhBNj6V0qxfJaX2lG+04pyon9JzPAS7qqsSo2WYNsfzXdHfU0QgkbUo23+8sglAPwHyJZqnLA7FZmtYsIXQSWnljppoDucwuXc2zZdhLzdsIXPJ/WK9VMEafQ9VbE4UrXzdNSM8cclguFk57rUulw0qwJZLDTlV5GoV4wyJj07TVKIMHKUO9nBpYOZNHac7TdAdV8yX1gWvK/dJmhem/YIWgTc/V70MncZ5zSsTXT74/2SDzptTDdeuRws9mcGA7Tk8SCbwxCHEJstoH0OHsmvl6tPaBZjZ3oyaIf7hLv+t1Qx9thm6WJh/hiatrWcCte/TAJz6lbDDMXplP2llEY7Up+sOeaf/LmIpV5YA4LHz0rQUCuqDv/3x7cPdeIW3P+0FXv8kVRq1w+RGKXF9E+KMK7pSGDhBmTmi/+lb+vfeSvm+cqY3gXao6eyyY4nUA1xKTh+nHp16cW7vFNWLSW/OS78Fz4AaUSBIh9N4RitBeRaUZGAX/y5iKVeWAOCx89K0FArqg7/98e3D3XiFtz/tBV7/JFUatcPkRilxfRPijCu6Uhg4QZk5ov/pW/r33kr5vnKmMnY6uIBccjRlbxEHDV/Pxk4yolUn09qYCD/QF8meoxb/YRouOdqcF3dM4YCQFiiHuBAUqWjmmS6ORk2+s6QMZq6rOkhIfX3nQBwCAaFsdGauo1i7w+8JMNAPGgFdBWa5qdmxfQ4koiLIIcAA0ZjeMLVl9odLt/hUntHti9gP9G8S6Z//YlaHcgV7zU+WWHWz8F3HlD3226SRRgtKpkfKBCAaUSBIh9N4RitBeRaUZGAX/y5iKVeWAOCx89K0FArqg7/98e3D3XiFtz/tBV7/JFUatcPkRilxfRPijCu6Uhg4QZk5ov/pW/r33kr5vnKmPOJUtlqH05/GyBWp8GY++ZkoGkBytjKq9jmkXvkvjlSUArwxm9g1VVzl51aKwMXd6G0wrErJZcmwmiIpB2tNqsjH5Ks+/n8n3Q7veteeIq3LoGVfxvLsVfrKyOpKjCKanjSrRYBI2kHucrqJYjAG1CXOHrL0nfciXcIE6SFb/waxeHLNdDlQvi9RlAH9Sc76vvZwaWDmTR2nO03QHVfMl94ZqyVQvQc6b57RFVpqMlH9DJ3Gec0rE10++P9kg86bUw3XrkcLPZnBgO05PEgm8MQhxCbLaB9Dh7Jr5erT2gWbYOhsYV7FEfaVYHAX4BeREWndxjPEeRZbI6nwwK8D+6+BsCXzW25lwYpbTpvTXcVUTCUmBL5LmPZ/7AQpQ/7FNuXcmr3sGDCU6EqoG4E5rlVVXVVCmGwj0eDfRz6/ZOsYaqtgZOpXtP6hTXUXVSYXY=";
|
|
||||||
//var baseString = "wQb/V5e6u70LZd5g1C1ZulicB4/KTXGAErDvWW0VdpuxrJf87bxEqN4dq55t2m5t530teJLczZM31JJLVwDMzKx9jvmxQNZ/Vo1abXxWamEpvmRRF1fHw2rOFK98VpWP";
|
|
||||||
var baseString = "dv+uxe7a/WVJ2zEC/uXbjeaw7sHDsRrXYe8gBH5VzCI44PFFIZuoHRbeSDNDSkJBGBybgGJogLtg0tT2apZEWJriB98GjeeZV7Gh7qaNXOxOLH9+Snnc9NPpqNqMTRLiGjR1pvvX9rjJXQAF+d2DrMtsB4RsbyKiJBPeWxWiI6jaPtyDG5E6fS/ErOTHQnxDH7JIhFBwTg0p8FVvIBlNoQ==";
|
|
||||||
System.out.println(decodeVeloBs(baseString));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
src/main/java/Main.kt
Normal file
19
src/main/java/Main.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.cio.*
|
||||||
|
import io.ktor.server.engine.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
fun main(){
|
||||||
|
val server = embeddedServer(CIO, port = 8080){
|
||||||
|
routing {
|
||||||
|
get("/"){
|
||||||
|
call.respondRedirect("/search")
|
||||||
|
}
|
||||||
|
searchPage()
|
||||||
|
trackPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("connection to localhost:8080 now possible.")
|
||||||
|
server.start(true)
|
||||||
|
}
|
||||||
89
src/main/java/SearchPage.kt
Normal file
89
src/main/java/SearchPage.kt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.html.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import io.ktor.util.pipeline.*
|
||||||
|
import kotlinx.html.*
|
||||||
|
|
||||||
|
fun Routing.searchPage(){
|
||||||
|
get("/search"){
|
||||||
|
searchPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/search"){
|
||||||
|
val parameters = call.receiveParameters()
|
||||||
|
val author = parameters["author"] ?: ""
|
||||||
|
val track = parameters["track"] ?: ""
|
||||||
|
val results = Velocidrone.getTrackResults(track,author)
|
||||||
|
results.getOrNull()?.let {
|
||||||
|
searchResultPage(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun PipelineContext<Unit,ApplicationCall>.searchPage(){
|
||||||
|
call.respondHtml {
|
||||||
|
head {
|
||||||
|
title {
|
||||||
|
+"Velocidrone track search"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
form("/search", encType = FormEncType.applicationXWwwFormUrlEncoded, method = FormMethod.post){
|
||||||
|
p{
|
||||||
|
+"Trackname:"
|
||||||
|
textInput(name = "track")
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
+"Author:"
|
||||||
|
textInput(name = "author")
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
submitInput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun PipelineContext<Unit,ApplicationCall>.searchResultPage(trackResults: Velocidrone.TrackResults){
|
||||||
|
call.respondHtml {
|
||||||
|
head {
|
||||||
|
title {
|
||||||
|
+"Search results"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
+"MapID"
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
+"Name"
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
+"Author"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackResults.user_tracks.forEach {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
a("/track/"+it.id){
|
||||||
|
+it.id.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
+it.track_name
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
+it.playername
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/main/java/TrackPage.kt
Normal file
42
src/main/java/TrackPage.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.html.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
import io.ktor.util.pipeline.*
|
||||||
|
import kotlinx.html.*
|
||||||
|
|
||||||
|
fun Routing.trackPage() {
|
||||||
|
get("/track/{id}"){
|
||||||
|
call.parameters.get("id")?.toLong()?.let {
|
||||||
|
val result = Velocidrone.getLeaderboardForId(it)
|
||||||
|
result.getOrNull()?.let {
|
||||||
|
trackPage(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun PipelineContext<Unit, ApplicationCall>.trackPage(leaderboard: Velocidrone.Leaderboard){
|
||||||
|
call.respondHtml {
|
||||||
|
head {
|
||||||
|
title {
|
||||||
|
+"Leaderboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
td { +"Time" }
|
||||||
|
td { +"Name" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaderboard.tracktimes.sortedBy { it.lap_time }.forEach {
|
||||||
|
tr {
|
||||||
|
td { +it.lap_time.toString() }
|
||||||
|
td { +it.playername }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/main/java/Velocidrone.kt
Normal file
97
src/main/java/Velocidrone.kt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.engine.cio.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.net.URLEncoder
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
object Velocidrone{
|
||||||
|
private val key = "BatCaveGGevaCtaB".toByteArray(Charset.defaultCharset())
|
||||||
|
|
||||||
|
fun decodeVeloBase64(input: ByteArray): Result<String> {
|
||||||
|
val decoded = Base64.getDecoder().decode(input)
|
||||||
|
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"))
|
||||||
|
return Result.success(String(cipher.doFinal(decoded)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encodeVeloBase64(input: String): ByteArray {
|
||||||
|
val encoded: ByteArray = input.toByteArray()
|
||||||
|
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"))
|
||||||
|
return Base64.getEncoder().encode(cipher.doFinal(encoded))
|
||||||
|
}
|
||||||
|
|
||||||
|
val client = HttpClient(CIO)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Leaderboard(
|
||||||
|
val success:Boolean,
|
||||||
|
val message_title:String,
|
||||||
|
val message:String,
|
||||||
|
val tracktimes:Array<TrackTime>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TrackTime(
|
||||||
|
val lap_time:Float,
|
||||||
|
val playername: String,
|
||||||
|
val model_id: Long,
|
||||||
|
val country: String,
|
||||||
|
val sim_version: String,
|
||||||
|
val device_type: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun getLeaderboardForId(id:Long): Result<Leaderboard> {
|
||||||
|
//track_id=21192&sim_version=1.16&offset=0&count=400&protected_track_value=2&race_mode=6
|
||||||
|
val packet = "track_id=$id&sim_version=1.16&offset=0&count=400&protected_track_value=2&race_mode=6"
|
||||||
|
|
||||||
|
val result = decodeVeloBase64(client.post("https://www.velocidrone.com/api/leaderboard/getLeaderBoard"){
|
||||||
|
val string = "post_data="+URLEncoder.encode(String(encodeVeloBase64(packet)), Charsets.UTF_8)
|
||||||
|
setBody(string)
|
||||||
|
setAttributes {
|
||||||
|
headers["Content-Type"]="application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
}.body())
|
||||||
|
|
||||||
|
return Result.success(Json.decodeFromString<Leaderboard>(result.getOrThrow()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TrackResults(
|
||||||
|
val success:Boolean,
|
||||||
|
val detail:String,
|
||||||
|
val user_tracks:Array<Track>
|
||||||
|
)
|
||||||
|
@Serializable
|
||||||
|
data class Track(
|
||||||
|
val id:Long,
|
||||||
|
val scenery_id:Long,
|
||||||
|
val track_name:String,
|
||||||
|
val track_type:String,
|
||||||
|
val playername:String,
|
||||||
|
val rating:Float,
|
||||||
|
val total_ratings:Long,
|
||||||
|
val date:String
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun getTrackResults(name:String, author:String): Result<TrackResults> {
|
||||||
|
//scenery_id=&playername=&track_name=&track_type=&order_by_date=True&order_by_rating=True&show_beginner=True&show_intermediate=True&show_advanced=True
|
||||||
|
//scenery_id=&playername=stimpyten&track_name=&track_type=&order_by_date=True&order_by_rating=True&show_beginner=True&show_intermediate=True&show_advanced=True
|
||||||
|
|
||||||
|
val packet = "scenery_id=&playername=$author&track_name=$name&track_type=&order_by_date=True&order_by_rating=True&show_beginner=True&show_intermediate=True&show_advanced=True"
|
||||||
|
|
||||||
|
return Result.success(Json.decodeFromString<TrackResults>(decodeVeloBase64(client.post("https://www.velocidrone.com/api/v1/private/user/rated_tracks_list"){
|
||||||
|
val string = "post_data="+URLEncoder.encode(String(encodeVeloBase64(packet)), Charsets.UTF_8)
|
||||||
|
setBody(string)
|
||||||
|
setAttributes {
|
||||||
|
headers["Content-Type"]="application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
}.body()).getOrThrow()))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user