mirror of
https://github.com/InsanusMokrassar/ConfigurableInlineTelegramBot.git
synced 2025-09-02 14:49:29 +00:00
init
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot
|
||||
|
||||
import dev.inmo.tgbotapi.bot.exceptions.RequestException
|
||||
import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery
|
||||
import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook
|
||||
import dev.inmo.configurable_inline_telegram_bot.config.BotConfig
|
||||
import dev.inmo.configurable_inline_telegram_bot.config.Restrictions
|
||||
import dev.inmo.configurable_inline_telegram_bot.models.OfferTemplate
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
|
||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
|
||||
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
suspend fun BehaviourContext.enableFormatterBot(
|
||||
templates: List<OfferTemplate>,
|
||||
restrictions: Restrictions? = null
|
||||
) {
|
||||
onBaseInlineQuery { query ->
|
||||
if (restrictions ?.check(query) == false) {
|
||||
answerInlineQuery(query, cachedTime = 0)
|
||||
return@onBaseInlineQuery
|
||||
}
|
||||
try {
|
||||
answerInlineQuery(
|
||||
query,
|
||||
templates.mapIndexedNotNull { index, offerTemplate ->
|
||||
offerTemplate.createArticleResult(
|
||||
index.toString(),
|
||||
query.query
|
||||
)
|
||||
},
|
||||
cachedTime = 0
|
||||
)
|
||||
} catch (e: RequestException) {
|
||||
bot.answerInlineQuery(
|
||||
query,
|
||||
cachedTime = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class FormatterBot(
|
||||
private val botConfig: BotConfig,
|
||||
private val templates: List<OfferTemplate>,
|
||||
private val restrictions: Restrictions? = null
|
||||
) {
|
||||
|
||||
suspend fun start(scope: CoroutineScope = GlobalScope) {
|
||||
val bot = botConfig.createBot()
|
||||
val filter = FlowsUpdatesFilter()
|
||||
bot.buildBehaviour(
|
||||
scope,
|
||||
filter
|
||||
) {
|
||||
enableFormatterBot(templates, restrictions)
|
||||
}
|
||||
botConfig.webhookConfig ?.setWebhookAndCreateServer(
|
||||
bot,
|
||||
filter,
|
||||
scope
|
||||
) ?.start(false) ?: bot.apply {
|
||||
scope.launch {
|
||||
deleteWebhook()
|
||||
longPolling(
|
||||
filter,
|
||||
exceptionsHandler = { it.printStackTrace() },
|
||||
scope = scope
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
val serialFormat = Json {
|
||||
ignoreUnknownKeys = true
|
||||
allowSpecialFloatingPointValues = true
|
||||
useArrayPolymorphism = true
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
fun main(vararg args: String) {
|
||||
val config = args.first()
|
||||
val bot = try {
|
||||
serialFormat.decodeFromString(
|
||||
FormatterBot.serializer(),
|
||||
config
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
File(config).readText().let {
|
||||
serialFormat.decodeFromString(
|
||||
FormatterBot.serializer(),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
val scope = CoroutineScope(Dispatchers.Default)
|
||||
scope.launch {
|
||||
bot.start(scope)
|
||||
}
|
||||
runBlocking {
|
||||
scope.coroutineContext[Job]!!.join()
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.config
|
||||
|
||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
||||
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
||||
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
|
||||
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.okhttp.OkHttp
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BotConfig(
|
||||
val botToken: String,
|
||||
val apiUrl: String = telegramBotAPIDefaultUrl,
|
||||
val clientConfig: HttpClientConfig? = null,
|
||||
val webhookConfig: WebhookConfig? = null
|
||||
) {
|
||||
fun createBot(): RequestsExecutor = telegramBot(
|
||||
botToken
|
||||
) {
|
||||
client = HttpClient(OkHttp.create(clientConfig ?.builder ?: {}))
|
||||
telegramAPIUrlsKeeper = TelegramAPIUrlsKeeper(
|
||||
botToken,
|
||||
apiUrl
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.config
|
||||
|
||||
import io.ktor.client.engine.okhttp.OkHttpConfig
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import okhttp3.*
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Serializable
|
||||
data class HttpClientConfig(
|
||||
val proxy: ProxySettings? = null,
|
||||
val connectTimeout: Long = 0,
|
||||
val writeTimeout: Long = 0,
|
||||
val readTimeout: Long = 0
|
||||
) {
|
||||
@Transient
|
||||
val builder: OkHttpConfig.() -> Unit = {
|
||||
config {
|
||||
this@HttpClientConfig.proxy ?.let {
|
||||
proxy(
|
||||
Proxy(
|
||||
Proxy.Type.SOCKS,
|
||||
InetSocketAddress(
|
||||
it.host,
|
||||
it.port
|
||||
)
|
||||
)
|
||||
)
|
||||
it.password ?.let { password ->
|
||||
proxyAuthenticator (
|
||||
object : Authenticator {
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
return response.request.newBuilder().apply {
|
||||
addHeader(
|
||||
"Proxy-Authorization",
|
||||
Credentials.basic(it.username ?: "", password)
|
||||
)
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
|
||||
writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
|
||||
readTimeout(readTimeout, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.config
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ProxySettings(
|
||||
val host: String = "localhost",
|
||||
val port: Int = 1080,
|
||||
val username: String? = null,
|
||||
val password: String? = null
|
||||
)
|
@@ -0,0 +1,14 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.config
|
||||
|
||||
import dev.inmo.tgbotapi.types.*
|
||||
import dev.inmo.tgbotapi.types.InlineQueries.abstracts.InlineQuery
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Restrictions(
|
||||
val allowedUsers: List<ChatIdentifier> = emptyList()
|
||||
) {
|
||||
fun check(query: InlineQuery): Boolean {
|
||||
return query.from.id in allowedUsers || query.from.username ?.let { it in allowedUsers } ?: false
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.config
|
||||
|
||||
import dev.inmo.tgbotapi.bot.RequestsExecutor
|
||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.setWebhookInfoAndStartListenWebhooks
|
||||
import dev.inmo.tgbotapi.requests.abstracts.toInputFile
|
||||
import dev.inmo.tgbotapi.requests.webhook.SetWebhook
|
||||
import dev.inmo.tgbotapi.updateshandlers.UpdatesFilter
|
||||
import dev.inmo.tgbotapi.updateshandlers.webhook.WebhookPrivateKeyConfig
|
||||
import io.ktor.server.engine.ApplicationEngine
|
||||
import io.ktor.server.tomcat.Tomcat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@Serializable
|
||||
data class WebhookConfig(
|
||||
val url: String,
|
||||
val listenHost: String = "0.0.0.0",
|
||||
val listenRoute: String = "/",
|
||||
val port: Int = System.getenv("PORT").toInt(),
|
||||
val certificatePath: String? = null,
|
||||
val maxConnections: Int = 40,
|
||||
val privateKeyConfig: WebhookPrivateKeyConfig? = null
|
||||
) {
|
||||
init {
|
||||
println(this)
|
||||
}
|
||||
|
||||
suspend fun setWebhookAndCreateServer(
|
||||
requestsExecutor: RequestsExecutor,
|
||||
filter: UpdatesFilter,
|
||||
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(maxConnections / 2).asCoroutineDispatcher())
|
||||
): ApplicationEngine = (certificatePath ?.let {
|
||||
requestsExecutor.setWebhookInfoAndStartListenWebhooks(
|
||||
port,
|
||||
Tomcat,
|
||||
SetWebhook(url, File(it).toInputFile(), maxAllowedConnections = maxConnections, allowedUpdates = filter.allowedUpdates),
|
||||
{ throwable: Throwable ->
|
||||
throwable.printStackTrace()
|
||||
},
|
||||
listenHost,
|
||||
listenRoute,
|
||||
privateKeyConfig = privateKeyConfig,
|
||||
scope = scope,
|
||||
block = filter.asUpdateReceiver
|
||||
)
|
||||
} ?: requestsExecutor.setWebhookInfoAndStartListenWebhooks(
|
||||
port,
|
||||
Tomcat,
|
||||
SetWebhook(
|
||||
url,
|
||||
maxAllowedConnections = maxConnections,
|
||||
allowedUpdates = filter.allowedUpdates
|
||||
),
|
||||
{ throwable: Throwable ->
|
||||
throwable.printStackTrace()
|
||||
},
|
||||
listenHost,
|
||||
listenRoute,
|
||||
privateKeyConfig = privateKeyConfig,
|
||||
scope = scope,
|
||||
block = filter.asUpdateReceiver
|
||||
)
|
||||
).also {
|
||||
it.environment.connectors.forEach {
|
||||
println(it)
|
||||
}
|
||||
it.start(false)
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.models
|
||||
|
||||
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
|
||||
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@Serializable
|
||||
data class Format(
|
||||
val template: String,
|
||||
val regexTemplate: String = "^$",
|
||||
val splitBy: String? = null,
|
||||
val enableMarkdownSupport: Boolean = false
|
||||
) {
|
||||
@Transient
|
||||
val queryRegex = Regex(regexTemplate, RegexOption.DOT_MATCHES_ALL)
|
||||
|
||||
init {
|
||||
println(queryRegex)
|
||||
}
|
||||
|
||||
fun formatByRegex(with: String): String? {
|
||||
return if (queryRegex.matches(with)) {
|
||||
template.format(*(splitBy ?.let { with.split(it).toTypedArray() } ?: arrayOf(with)))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun createContent(with: String): InputTextMessageContent? {
|
||||
return if (queryRegex.matches(with)) {
|
||||
InputTextMessageContent(
|
||||
template.format(*(splitBy ?.let { with.split(it).toTypedArray() } ?: arrayOf(with))),
|
||||
if (enableMarkdownSupport) MarkdownV2 else null
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package dev.inmo.configurable_inline_telegram_bot.models
|
||||
|
||||
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OfferTemplate(
|
||||
val title: String,
|
||||
val formats: List<Format> = emptyList(),
|
||||
val description: String? = null
|
||||
) {
|
||||
fun createArticleResult(id: String, query: String): InlineQueryResultArticle? = formats.firstOrNull {
|
||||
it.queryRegex.matches(query)
|
||||
} ?.createContent(query) ?.let { content ->
|
||||
InlineQueryResultArticle(
|
||||
id,
|
||||
title,
|
||||
content,
|
||||
description = description
|
||||
)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user