1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2026-01-08 06:19:16 +00:00

Compare commits

...

8 Commits

9 changed files with 90 additions and 7 deletions

View File

@@ -1,5 +1,15 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 0.38.17
* `Core`:
* Add `BotCommandScopeChat` as new `BotCommandScope` (fix of [#574](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/574))
## 0.38.16
* `Core`:
* `TelegramAPIUrlsKeeper` now have two new things: properties `webAppDataSecretKey` and fun `checkWebAppLink`
## 0.38.15 ## 0.38.15
* `Common`: * `Common`:

View File

@@ -20,6 +20,6 @@ javax_activation_version=1.1.1
dokka_version=1.6.10 dokka_version=1.6.10
library_group=dev.inmo library_group=dev.inmo
library_version=0.38.15 library_version=0.38.17
github_release_plugin_version=2.3.7 github_release_plugin_version=2.3.7

View File

@@ -22,11 +22,14 @@ private class SurrogateBotCommandScope(
BotCommandScopeAllGroupChats.type -> BotCommandScopeAllGroupChats BotCommandScopeAllGroupChats.type -> BotCommandScopeAllGroupChats
BotCommandScopeAllChatAdministrators.type -> BotCommandScopeAllChatAdministrators BotCommandScopeAllChatAdministrators.type -> BotCommandScopeAllChatAdministrators
BotCommandScopeChatAdministrators.type -> BotCommandScopeChatAdministrators( BotCommandScopeChatAdministrators.type -> BotCommandScopeChatAdministrators(
chatId ?: error("chat_administrators type must have $chatIdField field, but have no") chatId ?: error("${BotCommandScopeChatAdministrators.type} type must have $chatIdField field, but have no")
) )
BotCommandScopeChatMember.type -> BotCommandScopeChatMember( BotCommandScopeChatMember.type -> BotCommandScopeChatMember(
chatId ?: error("chat_administrators type must have $chatIdField field, but have no"), chatId ?: error("${BotCommandScopeChatMember.type} type must have $chatIdField field, but have no"),
userId ?: error("chat_administrators type must have $userIdField field, but have no") userId ?: error("${BotCommandScopeChatMember.type} type must have $userIdField field, but have no")
)
BotCommandScopeChat.type -> BotCommandScopeChat(
chatId ?: error("${BotCommandScopeChat.type} type must have $chatIdField field, but have no")
) )
else -> UnknownBotCommandScope(type) else -> UnknownBotCommandScope(type)
} }
@@ -40,6 +43,7 @@ private class SurrogateBotCommandScope(
BotCommandScopeAllChatAdministrators -> SurrogateBotCommandScope(scope.type) BotCommandScopeAllChatAdministrators -> SurrogateBotCommandScope(scope.type)
is BotCommandScopeChatAdministrators -> SurrogateBotCommandScope(scope.type, scope.chatId) is BotCommandScopeChatAdministrators -> SurrogateBotCommandScope(scope.type, scope.chatId)
is BotCommandScopeChatMember -> SurrogateBotCommandScope(scope.type, scope.chatId, scope.userId) is BotCommandScopeChatMember -> SurrogateBotCommandScope(scope.type, scope.chatId, scope.userId)
is BotCommandScopeChat -> SurrogateBotCommandScope(scope.type, scope.chatId)
} }
} }
} }
@@ -94,6 +98,17 @@ data class BotCommandScopeChatAdministrators(
} }
} }
@Serializable
data class BotCommandScopeChat(
override val chatId: ChatIdentifier
) : ChatBotCommandScope {
@Required
override val type: String = BotCommandScopeChat.type
companion object {
const val type = "chat"
}
}
@Serializable @Serializable
data class BotCommandScopeChatMember( data class BotCommandScopeChatMember(
override val chatId: ChatIdentifier, override val chatId: ChatIdentifier,

View File

@@ -0,0 +1,19 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.crypto.SourceBytes
import dev.inmo.micro_utils.crypto.SourceString
internal expect fun SourceString.hmacSha256(key: String): String
private val HEX_ARRAY = "0123456789abcdef".toCharArray()
internal fun SourceBytes.hex(): String {
val hexChars = CharArray(size * 2)
for (j in indices) {
val v: Int = this[j].toInt() and 0xFF
hexChars[j * 2] = HEX_ARRAY[v ushr 4]
hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F]
}
return hexChars.concatToString()
}
internal fun SourceString.hex(): String = encodeToByteArray().hex()

View File

@@ -18,6 +18,10 @@ class TelegramAPIUrlsKeeper(
token: String, token: String,
hostUrl: String = telegramBotAPIDefaultUrl hostUrl: String = telegramBotAPIDefaultUrl
) { ) {
val webAppDataSecretKey by lazy {
token.hmacSha256("WebAppData")
}
val commonAPIUrl: String val commonAPIUrl: String
val fileBaseUrl: String val fileBaseUrl: String
@@ -28,4 +32,10 @@ class TelegramAPIUrlsKeeper(
} }
fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath" fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath"
/**
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData]
* @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash]
*/
fun checkWebAppLink(rawData: String, hash: String) = rawData.hmacSha256(webAppDataSecretKey).hex() == hash
} }

View File

@@ -0,0 +1,6 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.crypto.CryptoJS
import dev.inmo.micro_utils.crypto.SourceString
actual fun SourceString.hmacSha256(key: String) = CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast<String>()

View File

@@ -0,0 +1,14 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.crypto.SourceString
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
actual fun SourceString.hmacSha256(key: String): String {
val mac = Mac.getInstance("HmacSHA256")
val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256")
mac.init(secretKey)
return mac.doFinal(toByteArray()).hex()
}

View File

@@ -27,6 +27,13 @@ repositories {
} }
kotlin { kotlin {
jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
js(IR) { js(IR) {
browser() browser()
nodejs() nodejs()

View File

@@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.webapps package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.crypto.CryptoJS import dev.inmo.micro_utils.crypto.CryptoJS
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
external class WebApp { external class WebApp {
val initData: String val initData: String
@@ -76,6 +77,7 @@ fun WebApp.onMainButtonClicked(eventHandler: EventHandler) = onEvent(EventType.M
*/ */
fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler) fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler)
fun WebApp.isInitDataSafe(botToken: String) = CryptoJS.hex( fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink(
CryptoJS.HmacSHA256(botToken, "WebAppData") initData,
) == initDataUnsafe.hash initDataUnsafe.hash
)