From 3f4dacc129da32d9403dc8df1f379d17771fba0f Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 2 Feb 2021 19:15:42 +0600 Subject: [PATCH] init --- .gitignore | 14 ++ Dockerfile | 9 + build.gradle | 47 +++++ deploy | 22 +++ gradle.properties | 8 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55436 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ publish.gradle | 75 ++++++++ publish.kpsb | 1 + settings.gradle | 1 + .../FormatterBot.kt | 77 ++++++++ .../SerialFormat.kt | 9 + .../StartBot.kt | 28 +++ .../config/BotConfig.kt | 27 +++ .../config/HttpClientConfig.kt | 51 ++++++ .../config/ProxySettings.kt | 11 ++ .../config/Restrictions.kt | 14 ++ .../config/WebhookConfig.kt | 71 ++++++++ .../models/Format.kt | 40 ++++ .../models/OfferTemplate.kt | 22 +++ 22 files changed, 789 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 build.gradle create mode 100755 deploy create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 publish.gradle create mode 100644 publish.kpsb create mode 100644 settings.gradle create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/FormatterBot.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/SerialFormat.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/StartBot.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/BotConfig.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/HttpClientConfig.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/ProxySettings.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/Restrictions.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/WebhookConfig.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/Format.kt create mode 100644 src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/OfferTemplate.kt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae097ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.idea +out/* +*.iml +target + +settings.xml + +.gradle/ +build/ +out/ +deploys/ + +config.json + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e50968e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM java:8 + +USER 1000 + +ADD ./build/distributions/configurable_inline_telegram_bot.tar / + +VOLUME /config.json + +ENTRYPOINT ["/configurable_inline_telegram_bot/bin/configurable_inline_telegram_bot", "/config.json"] diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..190a8bf --- /dev/null +++ b/build.gradle @@ -0,0 +1,47 @@ +project.group = "com.insanusmokrassar" + +buildscript { + repositories { + mavenLocal() + jcenter() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'application' +apply plugin: 'kotlinx-serialization' + +mainClassName="dev.inmo.configurable_inline_telegram_bot.StartBotKt" + +apply from: "publish.gradle" + +project.version = "$library_version" +project.group = "$library_group" + +repositories { + mavenLocal() + jcenter() + mavenCentral() + maven { url "https://kotlin.bintray.com/kotlinx" } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" + + api "io.ktor:ktor-client-core:$ktor_version" + api "io.ktor:ktor-client-okhttp:$ktor_version" + + api "io.ktor:ktor-server-core:$ktor_version" + api "io.ktor:ktor-server-tomcat:$ktor_version" + + // Use JUnit test framework + testImplementation 'junit:junit:4.12' +} diff --git a/deploy b/deploy new file mode 100755 index 0000000..69e0c40 --- /dev/null +++ b/deploy @@ -0,0 +1,22 @@ +#!/bin/bash + +function send_notification() { + echo "$1" +} + +function assert_success() { + "${@}" + local status=${?} + if [ ${status} -ne 0 ]; then + send_notification "### Error ${status} at: ${BASH_LINENO[*]} ###" + exit ${status} + fi +} + +app=planner-text-help +version=0.1 + +assert_success ./gradlew build +assert_success docker build -t $app:"$version" . +assert_success docker tag $app:"$version" $app +assert_success docker push $app diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..dbc9e1e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,8 @@ +kotlin.code.style=official +kotlin_version=1.4.21 + +telegram_bot_api_version=0.32.3 +ktor_version=1.5.1 + +library_version=0.1 +library_group=dev.inmo diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5971d8fb24e1f8389bccbfefbe4c3c599d717219 GIT binary patch literal 55436 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�M!C3? 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..24e21ec --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Feb 17 19:41:24 HKT 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4453cce --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/publish.gradle b/publish.gradle new file mode 100644 index 0000000..e6d9de0 --- /dev/null +++ b/publish.gradle @@ -0,0 +1,75 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + +task javadocsJar(type: Jar) { + classifier = 'javadoc' +} +task sourceJar (type : Jar) { + classifier = 'sources' +} + +afterEvaluate { + project.publishing.publications.all { + // rename artifacts + groupId "${project.group}" + if (it.name.contains('kotlinMultiplatform')) { + artifactId = "${project.name}" + artifact sourceJar + } else { + artifactId = "${project.name}-$name" + } + } +} + +publishing { + publications.all { + artifact javadocsJar + + pom { + description = "This library is fully ready to launch bot for working with presets of inline answers in Telegram via Bot API" + name = "${project.name}" + url = "https://insanusmokrassar.github.io/${project.name}" + + scm { + developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/${project.name}[push=]https://github.com/InsanusMokrassar/${project.name}" + url = "https://github.com/InsanusMokrassar/${project.name}" + } + + developers { + + developer { + id = "InsanusMokrassar" + name = "Ovsiannikov Aleksei" + email = "ovsyannikov.alexey95@gmail.com" + } + + } + + licenses { + + license { + name = "Apache Software License 2.0" + url = "https://opensource.org/licenses/Apache-2.0" + } + + } + } + + repositories { + maven { + name = "bintray" + url = uri("https://api.bintray.com/maven/${project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')}/insanusmokrassar/${project.name}/;publish=1;override=1") + credentials { + username = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') + password = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') + } + } + } + + } +} + +signing { + useGpgCmd() + publishing.publications.forEach { sign it } +} diff --git a/publish.kpsb b/publish.kpsb new file mode 100644 index 0000000..9d00fa6 --- /dev/null +++ b/publish.kpsb @@ -0,0 +1 @@ +{"bintrayConfig":{"repo":"insanusmokrassar","packageName":"${project.name}","packageVcs":"https://github.com/InsanusMokrassar/${project.name}","autoPublish":true,"overridePublish":true},"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://opensource.org/licenses/Apache-2.0"}],"mavenConfig":{"name":"${project.name}","description":"This library is fully ready to launch bot for working with presets of inline answers in Telegram via Bot API","url":"https://insanusmokrassar.github.io/${project.name}","vcsUrl":"https://github.com/InsanusMokrassar/${project.name}","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}]}} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..d0a7caa --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name='configurable_inline_telegram_bot' diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/FormatterBot.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/FormatterBot.kt new file mode 100644 index 0000000..4e1b7d3 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/FormatterBot.kt @@ -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, + 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, + 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 + ) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/SerialFormat.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/SerialFormat.kt new file mode 100644 index 0000000..1eb4ee7 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/SerialFormat.kt @@ -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 +} diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/StartBot.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/StartBot.kt new file mode 100644 index 0000000..7f30aa8 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/StartBot.kt @@ -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() + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/BotConfig.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/BotConfig.kt new file mode 100644 index 0000000..17e1205 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/BotConfig.kt @@ -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 + ) + } +} diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/HttpClientConfig.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/HttpClientConfig.kt new file mode 100644 index 0000000..e8cf500 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/HttpClientConfig.kt @@ -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) + } + } +} diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/ProxySettings.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/ProxySettings.kt new file mode 100644 index 0000000..866645f --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/ProxySettings.kt @@ -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 +) diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/Restrictions.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/Restrictions.kt new file mode 100644 index 0000000..2e25416 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/Restrictions.kt @@ -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 = emptyList() +) { + fun check(query: InlineQuery): Boolean { + return query.from.id in allowedUsers || query.from.username ?.let { it in allowedUsers } ?: false + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/WebhookConfig.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/WebhookConfig.kt new file mode 100644 index 0000000..cd72cf6 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/config/WebhookConfig.kt @@ -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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/Format.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/Format.kt new file mode 100644 index 0000000..1558698 --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/Format.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/OfferTemplate.kt b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/OfferTemplate.kt new file mode 100644 index 0000000..9cf5b4e --- /dev/null +++ b/src/main/kotlin/dev/inmo/configurable_inline_telegram_bot/models/OfferTemplate.kt @@ -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 = 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 + ) + } +}