diff --git a/.gitignore b/.gitignore index ad8ab87..d324ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.kotlin out/* *.iml target diff --git a/.template/bot/.env b/.template/bot/.env new file mode 100644 index 0000000..f0fba38 --- /dev/null +++ b/.template/bot/.env @@ -0,0 +1,2 @@ +title=$prompt +subtitle=Subtitle of {{$title}} diff --git a/.template/bot/{{$title}}/README.md b/.template/bot/{{$title}}/README.md new file mode 100644 index 0000000..b0995a1 --- /dev/null +++ b/.template/bot/{{$title}}/README.md @@ -0,0 +1,9 @@ +# {{$title}} + + + +## Launch + +```bash +../gradlew run --args="BOT_TOKEN" +``` \ No newline at end of file diff --git a/.template/bot/{{$title}}/build.gradle b/.template/bot/{{$title}}/build.gradle new file mode 100644 index 0000000..27a28d2 --- /dev/null +++ b/.template/bot/{{$title}}/build.gradle @@ -0,0 +1,22 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'application' + +mainClassName="{{$title}}Kt" + {{$subtitle}} + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} \ No newline at end of file diff --git a/.template/bot/{{$title}}/src/main/kotlin/{{$title}}.kt b/.template/bot/{{$title}}/src/main/kotlin/{{$title}}.kt new file mode 100644 index 0000000..4d2a0c0 --- /dev/null +++ b/.template/bot/{{$title}}/src/main/kotlin/{{$title}}.kt @@ -0,0 +1,34 @@ +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.LogLevel +import dev.inmo.kslog.common.defaultMessageFormatter +import dev.inmo.kslog.common.setDefaultKSLog +import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +/** + * This place can be the playground for your code. + */ +suspend fun main(vararg args: String) { + val botToken = args.first() + + val isDebug = args.any { it == "debug" } + + if (isDebug) { + setDefaultKSLog( + KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? -> + println(defaultMessageFormatter(level, tag, message, throwable)) + } + ) + } + + telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) { + // start here!! + val me = getMe() + println(me) + + allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) } + }.second.join() +} \ No newline at end of file diff --git a/.template/module_generator.main.kts b/.template/module_generator.main.kts new file mode 100755 index 0000000..c2fd924 --- /dev/null +++ b/.template/module_generator.main.kts @@ -0,0 +1,208 @@ +#!/usr/bin/env kotlin +/** + * Generates files and folders as they have been put in the folder. Envs uses common syntax, but + * values may contains {{${'$'}sampleVariable}} parts, where {{${'$'}sampleVariable}} will be replaced with variable value. + * Example: + * + * .env: + * sampleVariable=${'$'}prompt # require request from command line + * sampleVariable2=just some value + * sampleVariable3=${'$'}{sampleVariable}.${'$'}{sampleVariable2} + * + * Result variables: + * sampleVariable=your input in console # lets imagine you typed it + * sampleVariable2=just some value + * sampleVariable3=your input in console.just some value + * + * To use these variables in template, you will need to write {{${'$'}sampleVariable}}. + * You may use it in text of files as well as in files/folders names. + * + * Usage: kotlin generator.kts [args] folders... + * Args: + * -e, --env: Path to file with args for generation; Use "${'$'}prompt" as values to read variable value from console + * -o, --outputFolder: Folder where templates should be used. Folder of calling by default + * folders: Folders-templates + */ +import java.io.File + +val console = System.console() + +fun String.replaceWithVariables(envs: Map): String { + var currentString = this + var changed = false + + do { + changed = false + envs.forEach { (k, v) -> + val previousString = currentString + currentString = currentString.replace("{{$${k}}}", v) + changed = changed || currentString != previousString + } + } while (changed) + + return currentString +} + +fun requestVariable(variableName: String, defaultValue: String?): String { + console.printf("Enter value for variable $variableName${defaultValue ?.let { " [$it]" } ?: ""}: ") + return console.readLine().ifEmpty { defaultValue } ?: "" +} + +fun readEnvs(content: String, presets: Map): Map { + val initialEnvs = mutableMapOf() + content.split("\n").forEach { + val withoutComment = it.replace(Regex("\\#.*"), "") + + runCatching { + val (key, value) = withoutComment.split("=") + val existsValue = presets[key] + if (value == "\$prompt") { + initialEnvs[key] = requestVariable(key, existsValue) + } else { + initialEnvs[key] = requestVariable(key, value.replaceWithVariables(initialEnvs)) + } + } + } + var i = 0 + val readEnvs = initialEnvs.toMutableMap() + while (i < readEnvs.size) { + val key = readEnvs.keys.elementAt(i) + val currentValue = readEnvs.getValue(key) + val withReplaced = currentValue.replaceWithVariables(readEnvs) + var changed = false + if (withReplaced != currentValue) { + i = 0 + readEnvs[key] = withReplaced + } else { + i++ + } + } + return presets + readEnvs +} + +var envFile: File? = null +var outputFolder: File = File("./") // current folder by default +val templatesFolders = mutableListOf() +var extensions: List? = null + +fun readParameters() { + var i = 0 + while (i < args.size) { + val arg = args[i] + when (arg) { + "--env", + "-e" -> { + i++ + envFile = File(args[i]) + } + "--extensions", + "-ex" -> { + i++ + extensions = args[i].split(",") + } + "--outputFolder", + "-o" -> { + i++ + outputFolder = File(args[i]) + } + "--help", + "-h" -> { + println(""" + Generates files and folders as the have been put in the folder. Envs uses common syntax, but + values may contains {{${'$'}sampleVariable}} parts, where {{${'$'}sampleVariable}} will be replaced with variable value. + Example: + + .env: + sampleVariable=${'$'}prompt # require request from command line + sampleVariable2=just some value + sampleVariable3=${'$'}{sampleVariable}.${'$'}{sampleVariable2} + + Result variables: + sampleVariable=your input in console # lets imagine you typed it + sampleVariable2=just some value + sampleVariable3=your input in console.just some value + + To use these variables in template, you will need to write {{${'$'}sampleVariable}}. + You may use it in text of files as well as in files/folders names. + + Usage: kotlin generator.kts [args] folders... + Args: + -e, --env: Path to file with args for generation; Use "${'$'}prompt" as values to read variable value from console + -o, --outputFolder: Folder where templates should be used. Folder of calling by default + folders: Folders-templates + """.trimIndent()) + Runtime.getRuntime().exit(0) + } + else -> { + val potentialFile = File(arg) + println("Potential file/folder as template: ${potentialFile.absolutePath}") + runCatching { + if (potentialFile.exists()) { + println("Adding file/folder as template: ${potentialFile.absolutePath}") + templatesFolders.add(potentialFile) + } + }.onFailure { e -> + println("Unable to use folder $arg as template folder") + e.printStackTrace() + } + } + } + i++ + } +} + +readParameters() + +val envs: MutableMap = envFile ?.let { readEnvs(it.readText(), emptyMap()) } ?.toMutableMap() ?: mutableMapOf() + +println( + """ + Result environments: + ${envs.toList().joinToString("\n ") { (k, v) -> "$k=$v" }} + Result extensions: + ${extensions ?.joinToString()} + Input folders: + ${templatesFolders.joinToString("\n ") { it.absolutePath }} + Output folder: + ${outputFolder.absolutePath} + """.trimIndent() +) + +fun File.handleTemplate(targetFolder: File, envs: Map) { + println("Handling $absolutePath") + val localEnvs = File(absolutePath, ".env").takeIf { it.exists() } ?.let { + println("Reading .env in ${absolutePath}") + readEnvs(it.readText(), envs) + } ?: envs + println( + """ + Local environments: + ${localEnvs.toList().joinToString("\n ") { (k, v) -> "$k=$v" }} + """.trimIndent() + ) + val newName = name.replaceWithVariables(localEnvs) + println("New name $newName") + when { + !exists() -> return + isFile -> { + val content = useLines { + it.map { it.replaceWithVariables(localEnvs) }.toList() + }.joinToString("\n") + val targetFile = File(targetFolder, newName) + targetFile.writeText(content) + println("Target file: ${targetFile.absolutePath}") + } + else -> { + val folder = File(targetFolder, newName) + println("Target folder: ${folder.absolutePath}") + folder.mkdirs() + listFiles() ?.forEach { fileOrFolder -> + fileOrFolder.handleTemplate(folder, localEnvs) + } + } + } +} + +templatesFolders.forEach { folderOrFile -> + folderOrFile.handleTemplate(outputFolder, envs) +} diff --git a/gradle.properties b/gradle.properties index fd6383f..663bb62 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,8 +5,8 @@ org.gradle.jvmargs=-Xmx2344m kotlin.daemon.jvmargs=-Xmx2g -Xms500m -kotlin_version=1.9.23 -telegram_bot_api_version=15.3.0 -micro_utils_version=0.21.2 -serialization_version=1.6.3 +kotlin_version=2.0.0 +telegram_bot_api_version=16.0.0 +micro_utils_version=0.22.0 +serialization_version=1.7.1 ktor_version=2.3.11