Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experiment with User data
Browse files Browse the repository at this point in the history
orchestr7 committed Sep 7, 2024

Verified

This commit was signed with the committer’s verified signature.
zackkatz Zack Katz
1 parent f4a4d05 commit f2f05a8
Showing 23 changed files with 190 additions and 261 deletions.
4 changes: 3 additions & 1 deletion backend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -7,14 +7,16 @@ plugins {
}

dependencies {
api(project(":common"))
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-test")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
implementation("com.h2database:h2:2.3.232")
implementation("commons-codec:commons-codec:1.17.1")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
testImplementation(kotlin("test"))

This file was deleted.

This file was deleted.

15 changes: 0 additions & 15 deletions backend/src/jvmMain/kotlin/ru/posidata/backend/entity/User.kt

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

8 changes: 0 additions & 8 deletions backend/src/jvmMain/resources/application.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -43,38 +43,9 @@ class TelegramAuthController(private val telegramAuthService: TelegramAuthServic
//</body>
//</html>
// https://posidata.ru/api/test?id=221298772&first_name=Андрей&last_name=Кулешов&username=akuleshov7&photo_url=https%3A%2F%2Ft.me%2Fi%2Fuserpic%2F320%2Fd3fKyG306aXHDBCxZXfWTpGlii6fZqZMo1tBmMPEl_E.jpg&auth_date=1725656645&hash=742dda3a019e57821e1fb7acf9918aeb6f0d734cc5c8612913b6696aeab2c745
@GetMapping("/test")
fun test(
@RequestParam id: String,
@RequestParam first_name: String,
@RequestParam last_name: String,
@RequestParam photo_url: String,
@RequestParam username: String,
@RequestParam auth_date: String,
@RequestParam hash: String
): String {
println(username)
return username
}

@GetMapping("/get")
fun get(): String {
println("Hi!")
return "Hi"
}


@PostMapping("auth/telegram")
fun telegramAuth(@RequestBody request: Map<String, Any>): ResponseEntity<Any> {
return try {
val authResult = telegramAuthService.authenticate(request)
if (authResult.isAuthenticated) {
ResponseEntity.ok(authResult.token)
} else {
ResponseEntity.status(HttpStatus.FORBIDDEN).body("Login info hash mismatch")
}
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.FORBIDDEN).body("Server error while authenticating")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ru.posidata.backend.controller

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import ru.posidata.backend.service.TelegramAuthService
import ru.posidata.backend.service.UserService
import ru.posidata.common.ResourceType
import ru.posidata.common.Resources


@RestController
@RequestMapping("api")
class UserController(
private val telegramAuthService: TelegramAuthService,
private val userService: UserService
) {
@GetMapping("/get")
fun getResults(
@RequestParam username: String?,
@RequestParam telegramId: Long,
@RequestParam hash: String,
@RequestParam firstName: String?,
@RequestParam lastName: String?,
): ResponseEntity<Any> {
println("Received a request to get results from $username, $telegramId, $hash, $firstName, $lastName")
userService.findOrCreateUser(
username = username,
telegramId = telegramId,
firstName = firstName,
lastName = lastName
)
return ResponseEntity.status(HttpStatus.OK).body("")
}

@GetMapping("/answer")
fun submitAnswer(
@RequestParam pokemonName: String,
@RequestParam resourceType: ResourceType,
// @RequestParam gameNumber: Int,
@RequestParam username: String,
@RequestParam hash: String,
): ResponseEntity<Any> {
// user
println(pokemonName)
var count: Int = 0
val pokemon = Resources.getByName(pokemonName)
?: return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid pokemon name $pokemonName")
if (pokemon.type == resourceType) {
// correct answer
++count
} else {
// incorrect answer
}
return ResponseEntity.status(HttpStatus.OK).body(resourceType)
}
}
13 changes: 11 additions & 2 deletions backend/src/main/kotlin/ru/posidata/backend/entity/User.kt
Original file line number Diff line number Diff line change
@@ -9,7 +9,16 @@ data class User(
val id: Long = 0,
@Column(unique = true)
val telegramId: Long,
val firstName: String,
val firstName: String?,
val lastName: String?,
val username: String?
val username: String?,

@Column(nullable = true)
var firstGameScore: Int?,

@Column(nullable = true)
var secondGameScore: Int?,

@Column(nullable = true)
var thirdGameScore: Int?,
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package ru.posidata.backend.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import ru.posidata.backend.entity.User

@Repository
interface UserRepository : JpaRepository<User, Long> {
interface UserRepository : CrudRepository<User, Long> {
fun findByTelegramId(telegramId: Long): User?
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,58 @@
package ru.posidata.backend.service

import org.apache.commons.codec.digest.HmacUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.security.MessageDigest
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec


@Service
class TelegramAuthService(
@Value("\${telegram.bot.token}") private val telegramToken: String,
private val userService: UserService
) {
fun authenticate(request: Map<String, Any>): AuthResult {
val hash = request["hash"] as String
val sortedRequest = request.toMutableMap().apply { remove("hash") }

val dataCheckString = sortedRequest.entries
.sortedBy { it.key.lowercase(Locale.getDefault()) }
.joinToString("\n") { "${it.key}=${it.value}" }

val secretKey = SecretKeySpec(
MessageDigest.getInstance("SHA-256").digest(telegramToken.toByteArray(Charsets.UTF_8)),
"HmacSHA256"
)
val mac = Mac.getInstance("HmacSHA256")
mac.init(secretKey)

val result = mac.doFinal(dataCheckString.toByteArray(Charsets.UTF_8))
val calculatedHash = result.joinToString("") { "%02x".format(it) }

return if (hash.equals(calculatedHash, ignoreCase = true)) {
val user = userService.findOrCreateUser(request)
println(user)
AuthResult(true, "token")
} else {
AuthResult(false, null)
}
fun isValidHash(hash: String): Boolean {
val secreteKey = "1289756607:AAFYuBeHguJKYhDVzwSfYFLX4tF5YbkSU7M"
return true
}

}


fun main() {
println(isValidHash())
}

// Bot token
const val BOT_TOKEN = "7518590686:AAFCT7m70_ANfOZfIACr2C7bOy0igdLNOpg"

// Function to validate hash
fun isValidHash(): Boolean {
val hash = "742dda3a019e57821e1fb7acf9918aeb6f0d734cc5c8612913b6696aeab2c745"

val parsedData: Map<String, String> = mapOf(
"id" to "221298772",
"first_name" to "Андрей",
"last_name" to "Кулешов",
"username" to "akuleshov7",
"photo_url" to "https%3A%2F%2Ft.me%2Fi%2Fuserpic%2F320%2Fd3fKyG306aXHDBCxZXfWTpGlii6fZqZMo1tBmMPEl_E.jpg",
"auth_date" to "1725656645",
"hash" to "742dda3a019e57821e1fb7acf9918aeb6f0d734cc5c8612913b6696aeab2c745"
)

// Remove 'hash' value & sort alphabetically
val dataKeys = parsedData.keys.filter { it != "hash" }.sorted()

// Create line format key=<value>
val items = dataKeys.map { key -> "$key=${parsedData[key]}" }

// Create check string with '\n' as separator
val dataCheckString = items.joinToString("\n")

val secretKey: ByteArray = HmacUtils("HmacSHA256", "WebAppData").hmac(BOT_TOKEN)
val initDataHash: String = HmacUtils("HmacSHA256", secretKey).hmacHex(dataCheckString)

println(initDataHash)
println(hash)
// Return whether the generated hash matches the provided hash
return initDataHash == hash
}

data class AuthResult(val isAuthenticated: Boolean, val token: String?)
30 changes: 17 additions & 13 deletions backend/src/main/kotlin/ru/posidata/backend/service/UserService.kt
Original file line number Diff line number Diff line change
@@ -6,18 +6,22 @@ import ru.posidata.backend.repository.UserRepository

@Service
class UserService(private val userRepository: UserRepository) {
fun findOrCreateUser(telegramData: Map<String, Any>): User {
val telegramId = telegramData["id"] as Long
return userRepository.findByTelegramId(telegramId) ?: createUser(telegramData)
}

private fun createUser(telegramData: Map<String, Any>): User {
val user = User(
telegramId = telegramData["id"] as Long,
firstName = telegramData["first_name"] as String,
lastName = telegramData["last_name"] as? String,
username = telegramData["username"] as? String
fun findOrCreateUser(
username: String?,
telegramId: Long,
firstName: String?,
lastName: String?
): User {
return userRepository.findByTelegramId(telegramId) ?: userRepository.save(
User(
telegramId = telegramId,
username = username,
firstName = firstName,
lastName = lastName,
firstGameScore = 0,
secondGameScore = null,
thirdGameScore = null
)
)
return userRepository.save(user)
}
}
}
17 changes: 17 additions & 0 deletions backend/src/test/kotlin/ru/posidata/backend/UserControllerTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.posidata.backend

import ru.posidata.backend.controller.UserController

import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.springframework.http.HttpStatus
import ru.posidata.backend.service.TelegramAuthService
import ru.posidata.common.ResourceType
import org.junit.jupiter.api.Assertions.*
import org.mockito.Mockito.verifyNoInteractions

class UserControllerTest {

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ru.posidata.views.utils.internals
package ru.posidata.common

enum class Selection {
ANSWER,
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ru.posidata.views.components
package ru.posidata.common

import ru.posidata.views.components.ResourceType.POKEMON
import ru.posidata.views.components.ResourceType.BIG_DATA
import ru.posidata.common.ResourceType.POKEMON
import ru.posidata.common.ResourceType.BIG_DATA

enum class ResourceType {
POKEMON,
@@ -64,6 +64,7 @@ enum class Resources(

companion object {
fun getById(i: Int): Resources = Resources.entries.find { it.id == i }!!
fun getByName(name: String): Resources? = Resources.entries.find { it.name.uppercase() == name }
}

fun getName(): String {
14 changes: 7 additions & 7 deletions frontend/src/jsMain/kotlin/ru/posidata/views/main/AnswerCard.kt
Original file line number Diff line number Diff line change
@@ -10,13 +10,13 @@ import react.dom.html.ReactHTML.h2
import react.dom.html.ReactHTML.h6
import react.dom.html.ReactHTML.img
import react.useState
import ru.posidata.views.components.ResourceType.BIG_DATA
import ru.posidata.views.components.ResourceType.POKEMON
import ru.posidata.views.components.Resources
import ru.posidata.views.utils.internals.Answer
import ru.posidata.views.utils.internals.Selection
import ru.posidata.views.utils.internals.Selection.QUESTION
import ru.posidata.views.utils.internals.Selection.RESULTS
import ru.posidata.common.ResourceType.BIG_DATA
import ru.posidata.common.ResourceType.POKEMON
import ru.posidata.common.Resources
import ru.posidata.common.Answer
import ru.posidata.common.Selection
import ru.posidata.common.Selection.QUESTION
import ru.posidata.common.Selection.RESULTS
import web.cssom.*

val answerCard = FC<AnswerProps> { props ->
10 changes: 5 additions & 5 deletions frontend/src/jsMain/kotlin/ru/posidata/views/main/MainView.kt
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@ package ru.posidata.views.main
import js.objects.jso
import react.*
import react.dom.html.ReactHTML.div
import ru.posidata.views.utils.internals.Answer.NONE
import ru.posidata.views.utils.internals.Selection
import ru.posidata.views.utils.internals.Selection.QUESTION
import ru.posidata.views.utils.internals.Selection.ANSWER
import ru.posidata.views.utils.internals.Selection.RESULTS
import ru.posidata.common.Answer.NONE
import ru.posidata.common.Selection
import ru.posidata.common.Selection.QUESTION
import ru.posidata.common.Selection.ANSWER
import ru.posidata.common.Selection.RESULTS
import ru.posidata.views.utils.externals.particles.Particles
import ru.posidata.views.utils.externals.telegram.User
import web.cssom.*
12 changes: 6 additions & 6 deletions frontend/src/jsMain/kotlin/ru/posidata/views/main/ProgressBar.kt
Original file line number Diff line number Diff line change
@@ -7,12 +7,12 @@ import react.dom.html.ReactHTML.div
import ru.posidata.views.utils.externals.fontawesome.faThumbsUp
import ru.posidata.views.utils.externals.fontawesome.faXmark
import ru.posidata.views.utils.externals.fontawesome.fontAwesomeIcon
import ru.posidata.views.utils.internals.Answer.NONE
import ru.posidata.views.utils.internals.Answer.WRONG
import ru.posidata.views.utils.internals.Answer.CORRECT
import ru.posidata.views.utils.internals.Selection.QUESTION
import ru.posidata.views.utils.internals.Answer
import ru.posidata.views.utils.internals.Selection
import ru.posidata.common.Answer.NONE
import ru.posidata.common.Answer.WRONG
import ru.posidata.common.Answer.CORRECT
import ru.posidata.common.Selection.QUESTION
import ru.posidata.common.Answer
import ru.posidata.common.Selection
import web.cssom.*

val progressBar = FC<ProgressBarProps> { props ->
Original file line number Diff line number Diff line change
@@ -5,16 +5,16 @@ import react.*
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h1
import react.dom.html.ReactHTML.img
import ru.posidata.views.utils.internals.Answer.WRONG
import ru.posidata.views.utils.internals.Answer.CORRECT
import ru.posidata.views.utils.internals.Selection.ANSWER
import ru.posidata.views.components.ResourceType.BIG_DATA
import ru.posidata.views.components.ResourceType.POKEMON
import ru.posidata.views.components.Resources
import ru.posidata.common.Answer.WRONG
import ru.posidata.common.Answer.CORRECT
import ru.posidata.common.Selection.ANSWER
import ru.posidata.common.ResourceType.BIG_DATA
import ru.posidata.common.ResourceType.POKEMON
import ru.posidata.common.Resources
import ru.posidata.views.components.neonLightingText
import ru.posidata.views.utils.externals.telegram.User
import ru.posidata.views.utils.internals.Answer
import ru.posidata.views.utils.internals.Selection
import ru.posidata.common.Answer
import ru.posidata.common.Selection
import web.cssom.*
import kotlin.random.Random

Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import react.Props
import react.StateSetter
import react.dom.html.ReactHTML
import react.dom.html.ReactHTML.a
import ru.posidata.views.utils.internals.Answer.CORRECT
import ru.posidata.common.Answer.CORRECT
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h1
import react.dom.html.ReactHTML.h2
@@ -16,9 +16,9 @@ import react.useState
import ru.posidata.views.utils.externals.fontawesome.faGithub
import ru.posidata.views.utils.externals.fontawesome.fontAwesomeIcon
import ru.posidata.views.utils.externals.telegram.User
import ru.posidata.views.utils.internals.Answer
import ru.posidata.views.utils.internals.Answer.NONE
import ru.posidata.views.utils.internals.Selection
import ru.posidata.common.Answer
import ru.posidata.common.Answer.NONE
import ru.posidata.common.Selection
import web.cssom.*

val resultCard = FC<ResultProps> { props ->
3 changes: 2 additions & 1 deletion frontend/src/jsMain/kotlin/ru/posidata/views/main/Welcome.kt
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import react.dom.html.ReactHTML.h6
import react.dom.html.ReactHTML.img
import ru.posidata.views.utils.externals.telegram.TLoginButton
import ru.posidata.views.utils.externals.telegram.User
import ru.posidata.views.utils.internals.Selection
import ru.posidata.common.Selection
import web.cssom.*

val welcomeCard = FC<WelcomeCardProps> { props ->
@@ -82,6 +82,7 @@ val welcomeCard = FC<WelcomeCardProps> { props ->
botName = "PosiDataBot"
buttonSize = "large"
onAuthCallback = { user ->
console.log(user)
val feUser = User(
authDate = user.auth_date,
firstName = user.first_name,

0 comments on commit f2f05a8

Please sign in to comment.