Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
2ee2cdca10 Update dependency gradle to v8.8 2024-06-01 01:00:15 +00:00
52 changed files with 466 additions and 1348 deletions

View File

@@ -10,11 +10,10 @@ jobs:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y libcurl4-openssl-dev
- name: Set up JDK 17
uses: actions/setup-java@v1
with:
java-version: 17
- name: Build with Gradle
run: ./gradlew build --no-daemon
run: ./gradlew build

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.idea
.kotlin
out/*
*.iml
target

View File

@@ -1,2 +0,0 @@
title=$prompt
subtitle=Subtitle of {{$title}}

View File

@@ -1,9 +0,0 @@
# {{$title}}
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,22 +0,0 @@
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"
}

View File

@@ -1,34 +0,0 @@
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()
}

View File

@@ -1,208 +0,0 @@
#!/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, String>): 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<String, String>): Map<String, String> {
val initialEnvs = mutableMapOf<String, String>()
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<File>()
var extensions: List<String>? = 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<String, String> = 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<String, String>) {
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)
}

View File

@@ -4,13 +4,13 @@ import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.get.getUserChatBoosts
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatBoostUpdated
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatReplyKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestChannelButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.regular
import korlibs.time.DateFormat
@@ -52,19 +52,12 @@ suspend fun main(args: Array<String>) {
}
onChatShared(initialFilter = { it.chatEvent.requestId == requestChatId }) {
val boostsInfoContrainer = runCatching {
getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
}.getOrNull()
reply(it) {
when {
boostsInfoContrainer == null -> +"Unable to take info about boosts in shared chat"
boostsInfoContrainer.boosts.isEmpty() -> +"There is no any boosts in passed chat"
else -> {
boostsInfoContrainer.boosts.forEach {
regular("Boost added: ${DateFormat.FORMAT1.format(it.addDate.asDate)}; Boost expire: ${DateFormat.FORMAT1.format(it.expirationDate.asDate)}; Unformatted: $it") + "\n"
}
}
val boosts = getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
reply(
it
) {
boosts.boosts.forEach {
regular("Boost added: ${DateFormat.FORMAT1.format(it.addDate.asDate)}; Boost expire: ${DateFormat.FORMAT1.format(it.expirationDate.asDate)}; Unformatted: $it") + "\n"
}
}
}

View File

@@ -3,17 +3,12 @@ import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.modify.pinChatMessage
import dev.inmo.tgbotapi.extensions.api.chat.modify.unpinChatMessage
import dev.inmo.tgbotapi.extensions.api.get.getBusinessConnection
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.accessibleMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.ifAccessibleMessage
import dev.inmo.tgbotapi.extensions.utils.ifBusinessContentMessage
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
import kotlinx.coroutines.CoroutineScope
@@ -54,34 +49,22 @@ suspend fun main(args: Array<String>) {
}
onContentMessage {
it.ifBusinessContentMessage { businessContentMessage ->
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/pin") == true) {
businessContentMessage.replyTo ?.ifAccessibleMessage {
pinChatMessage(it)
return@ifBusinessContentMessage
}
}
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/unpin") == true) {
businessContentMessage.replyTo ?.ifAccessibleMessage {
unpinChatMessage(it)
return@ifBusinessContentMessage
}
}
val sent = execute(it.content.createResend(businessContentMessage.from.id))
if (businessContentMessage.sentByBusinessConnectionOwner) {
reply(sent, "You have sent this message to the ${businessContentMessage.businessConnectionId.string} related chat")
it.ifBusinessContentMessage {
val sent = execute(it.content.createResend(it.from.id))
if (it.sentByBusinessConnectionOwner) {
reply(sent, "You have sent this message to the ${it.businessConnectionId.string} related chat")
} else {
reply(sent, "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat")
reply(sent, "User have sent this message to you in the ${it.businessConnectionId.string} related chat")
}
}
}
onEditedContentMessage {
it.ifBusinessContentMessage { businessContentMessage ->
val sent = execute(businessContentMessage.content.createResend(businessContentMessage.from.id))
if (businessContentMessage.sentByBusinessConnectionOwner) {
reply(sent, "You have edited this message in the ${businessContentMessage.businessConnectionId.string} related chat")
it.ifBusinessContentMessage {
val sent = execute(it.content.createResend(it.from.id))
if (it.sentByBusinessConnectionOwner) {
reply(sent, "You have edited this message in the ${it.businessConnectionId.string} related chat")
} else {
reply(sent, "User have edited this message to you in the ${businessContentMessage.businessConnectionId.string} related chat")
reply(sent, "User have edited this message to you in the ${it.businessConnectionId.string} related chat")
}
}
}

View File

@@ -5,9 +5,9 @@ import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.*
suspend fun main(args: Array<String>) {
val bot = telegramBot(args.first())

View File

@@ -4,24 +4,18 @@ 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.BehaviourContextData
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
private var BehaviourContextData.update
get() = get("update")
set(value) = set("update", value)
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.*
/**
* This place can be the playground for your code.
* The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
*/
@OptIn(PreviewFeature::class)
suspend fun main(vararg args: String) {
val botToken = args.first()
val isDebug = args.any { it == "debug" }
val isTestServer = args.any { it == "testServer" }
if (isDebug) {
setDefaultKSLog(
@@ -31,34 +25,9 @@ suspend fun main(vararg args: String) {
)
}
telegramBotWithBehaviourAndLongPolling(
botToken,
CoroutineScope(Dispatchers.IO),
testServer = isTestServer,
builder = {
includeMiddlewares {
addMiddleware {
doOnRequestReturnResult { result, request, _ ->
println("Result of $request:\n\n$result")
null
}
}
}
},
subcontextInitialAction = {
data.update = it
}
) {
// start here!!
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
println(me)
onCommand("start") {
println(data.update)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

View File

@@ -1,11 +1,11 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelySkippingExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDeepLink
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramDeepLink
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource

View File

@@ -1,20 +1,23 @@
import dev.inmo.micro_utils.coroutines.AccumulatorFlow
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndFSMAndStartLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.command
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread
import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.botCommand
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first

View File

@@ -2,17 +2,38 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.replyWithAnimation
import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio
import dev.inmo.tgbotapi.extensions.api.send.replyWithDocument
import dev.inmo.tgbotapi.extensions.api.send.replyWithMediaGroup
import dev.inmo.tgbotapi.extensions.api.send.replyWithPhoto
import dev.inmo.tgbotapi.extensions.api.send.replyWithSticker
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideo
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideoNote
import dev.inmo.tgbotapi.extensions.api.send.replyWithVoice
import dev.inmo.tgbotapi.extensions.api.send.withAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.types.actions.*
import dev.inmo.tgbotapi.requests.send.SendAction
import dev.inmo.tgbotapi.types.actions.BotAction
import dev.inmo.tgbotapi.types.actions.TypingAction
import dev.inmo.tgbotapi.types.media.TelegramMediaAudio
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.AnimationContent
import dev.inmo.tgbotapi.types.message.content.AudioContent
import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.PhotoContent
import dev.inmo.tgbotapi.types.message.content.StickerContent
import dev.inmo.tgbotapi.types.message.content.VideoContent
import dev.inmo.tgbotapi.types.message.content.VideoNoteContent
import dev.inmo.tgbotapi.types.message.content.VoiceContent
import dev.inmo.tgbotapi.utils.filenameFromUrl
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -34,27 +55,13 @@ suspend fun main(args: Array<String>) {
val content = it.content
val pathedFile = bot.getFileAdditionalInfo(content.media)
val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl)
withTypingAction(it.chat.id) {
runCatching {
bot.downloadFile(content.media, outFile)
}.onFailure {
it.printStackTrace()
}.onSuccess { _ ->
reply(it, "Saved to ${outFile.absolutePath}")
}
runCatching {
bot.downloadFile(content.media, outFile)
}.onFailure {
it.printStackTrace()
}.onSuccess { _ ->
val action = when (content) {
is PhotoContent -> UploadPhotoAction
is AnimationContent,
is VideoContent -> UploadVideoAction
is StickerContent -> ChooseStickerAction
is MediaGroupContent<*> -> UploadPhotoAction
is DocumentContent -> UploadDocumentAction
is VoiceContent,
is AudioContent -> RecordVoiceAction
is VideoNoteContent -> UploadVideoNoteAction
}
withAction(it.chat.id, action) {
reply(it, "Saved to ${outFile.absolutePath}")
withAction(it.chat.id, TypingAction) {
when (content) {
is PhotoContent -> replyWithPhoto(
it,

View File

@@ -1,13 +1,15 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.filter.filtered
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
/**
* This is one of the easiest bots - it will just print information about itself
* This is one of the most easiest bot - it will just print information about itself
*/
suspend fun main(vararg args: String) {
val botToken = args.first()

View File

@@ -1,9 +0,0 @@
# CustomBot
Printing giveaways
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="GiveawaysBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,57 +0,0 @@
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 dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCompleted
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayContent
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCreated
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayWinners
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" }
val isTestServer = args.any { it == "testServer" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
telegramBotWithBehaviourAndLongPolling(botToken, testServer = isTestServer) {
// start here!!
val me = getMe()
println(me)
onGiveawayCreated {
println(it)
}
onGiveawayCompleted {
println(it)
}
onGiveawayWinners {
println(it)
}
onGiveawayContent {
println(it)
}
// allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
// println(it)
// }
}.second.join()
}

View File

@@ -1,21 +1,23 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMentionWithAnyContent
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.text
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.chat.SupergroupChat
import dev.inmo.tgbotapi.types.message.MarkdownV2
import dev.inmo.tgbotapi.utils.PreviewFeature
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.*
/**
* The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
@@ -26,35 +28,24 @@ suspend fun main(vararg args: String) {
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
onContentMessage(
initialFilter = initialFilter@{ it.text ?.contains(me.username ?.full ?: return@initialFilter false) == true }
) { message ->
onMentionWithAnyContent(me) { message ->
val chat = message.chat
val answerText = when (val chat = message.chat) {
is PreviewChannelChat -> {
val sender = message.sender_chat
val answer = "Hi everybody in this channel \"${chat.title}\"" + if (sender != null) {
" and you, " + when (sender) {
is BusinessChat -> "business chat (wat) ${sender.original}"
is PrivateChat -> "${sender.lastName} ${sender.firstName}"
is GroupChat -> "group ${sender.title}"
is ChannelChat -> "channel ${sender.title}"
is UnknownChatType -> "wat chat (${sender})"
}
} else {
""
}
reply(message, answer.escapeMarkdownV2Common(), MarkdownV2)
return@onContentMessage
val answer = "Hi everybody in this channel \"${chat.title}\""
reply(message, answer, MarkdownV2)
return@onMentionWithAnyContent
}
is PreviewPrivateChat -> {
reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2)
return@onContentMessage
return@onMentionWithAnyContent
}
is PreviewGroupChat -> {
message.ifFromChannelGroupContentMessage<Unit> {
message.ifFromChannelGroupContentMessage {
val answer = "Hi, ${it.senderChat.title}"
reply(message, answer, MarkdownV2)
return@onContentMessage
return@onMentionWithAnyContent
}
"Oh, hi, " + when (chat) {
is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let {
@@ -67,7 +58,7 @@ suspend fun main(vararg args: String) {
}
is PreviewBusinessChat -> {
reply(message, "Hi, " + "${chat.original.firstName} ${chat.original.lastName} (as business chat :) )".textMentionMarkdownV2(chat.original.id), MarkdownV2)
return@onContentMessage
return@onMentionWithAnyContent
}
is UnknownChatType -> "Unknown :(".escapeMarkdownV2Common()
}

View File

@@ -33,6 +33,6 @@ kotlin {
}
dependencies {
implementation 'io.ktor:ktor-client-logging-jvm:3.0.1'
implementation 'io.ktor:ktor-client-logging-jvm:2.3.7'
}

View File

@@ -1,3 +1,5 @@
import dev.inmo.micro_utils.common.MPPFile
suspend fun main(args: Array<String>) {
doInlineQueriesBot(args.first())
}

View File

@@ -1,12 +1,14 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.editMessageText
import dev.inmo.tgbotapi.extensions.api.edit.reply_markup.editMessageReplyMarkup
import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.withContent
@@ -15,13 +17,11 @@ import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResult
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.InlineQueryId
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.PreviewFeature
import dev.inmo.tgbotapi.utils.botCommand
import dev.inmo.tgbotapi.utils.regular
import dev.inmo.tgbotapi.utils.row
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import dev.inmo.tgbotapi.utils.*
import kotlinx.coroutines.*
private const val nextPageData = "next"
private const val previousPageData = "previous"
fun String.parsePageAndCount(): Pair<Int, Int>? {
val (pageString, countString) = split(" ").takeIf { it.count() > 1 } ?: return null
@@ -45,9 +45,6 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
}
}
}
row {
copyTextButton("Command copy button", "/inline $page $count")
}
row {
if (page - 1 > 2) {
@@ -76,7 +73,6 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
}
}
@OptIn(PreviewFeature::class)
suspend fun activateKeyboardsBot(
token: String,
print: (Any) -> Unit
@@ -87,13 +83,11 @@ suspend fun activateKeyboardsBot(
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onCommandWithArgs("inline") { message, args ->
val numberArgs = args.mapNotNull { it.toIntOrNull() }
val numberOfPages = numberArgs.getOrNull(1) ?: numberArgs.firstOrNull() ?: 10
val page = numberArgs.firstOrNull() ?.takeIf { numberArgs.size > 1 } ?.coerceAtLeast(1) ?: 1
val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10
reply(
message,
replyMarkup = inlineKeyboard {
includePageButtons(page, numberOfPages)
includePageButtons(1, numberOfPages)
}
) {
regular("Your inline keyboard with $numberOfPages pages")

View File

@@ -1,21 +1,7 @@
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun main(args: Array<String>) {
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
withContext(Dispatchers.IO) { // IO for inheriting of it in side of activateKeyboardsBot
activateKeyboardsBot(args.first()) {
println(it)

View File

@@ -3,15 +3,24 @@ import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.copyMessage
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionsCountUpdated
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.textLinkTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.uRLTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.LinkPreviewOptions
import dev.inmo.tgbotapi.types.chat.ExtendedChat
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextedContent
import dev.inmo.tgbotapi.types.reactions.Reaction
import dev.inmo.tgbotapi.utils.customEmoji
import dev.inmo.tgbotapi.utils.regular
/**
@@ -32,17 +41,17 @@ suspend fun main(vararg args: String) {
val bot = telegramBot(botToken)
bot.buildBehaviourWithLongPolling {
onContentMessage { contentMessage ->
val url = contentMessage.withContentOrNull<TextedContent>() ?.let { message ->
message.content.textSources.firstNotNullOfOrNull {
onContentMessage {
val url = it.withContentOrNull<TextedContent>() ?.let {
it.content.textSources.firstNotNullOfOrNull {
it.textLinkTextSourceOrNull() ?.url ?: it.uRLTextSourceOrNull() ?.source
}
} ?: null.apply {
reply(contentMessage) {
reply(it) {
regular("I am support only content with text contains url only")
}
} ?: return@onContentMessage
contentMessage.withContentOrNull<TextedContent>() ?.let {
it.withContentOrNull<TextedContent>() ?.let {
send(
it.chat,
it.content.textSources,

View File

@@ -8,6 +8,7 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onComman
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.location.LiveLocation
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.LocationContent
import kotlinx.coroutines.*
@@ -46,7 +47,7 @@ suspend fun main(vararg args: String) {
handleLiveLocation(
it.chat.id,
locationsFlow,
sentMessageFlow = { currentMessageState.emit(it) },
sentMessageFlow = FlowCollector { currentMessageState.emit(it) },
)
}

View File

@@ -1,10 +0,0 @@
# MemberUpdatedWatcherBot
This bot will watch for some ChatMemberUpdated events using new extensions from 18.0.0
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="MemberUpdatedWatcherKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,98 +0,0 @@
import dev.inmo.kslog.common.*
import dev.inmo.tgbotapi.extensions.api.*
import dev.inmo.tgbotapi.extensions.api.bot.*
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictedFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictionsChangedFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.*
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.utils.*
@OptIn(PreviewFeature::class)
suspend fun main(args: Array<String>) {
val token = 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))
}
)
}
val internalLogger = KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag ?: "ChatMemberUpdates", message, throwable))
}
val bot = telegramBot(token)
bot.buildBehaviourWithLongPolling {
val me = getMe()
val filterSelfUpdates = SimpleFilter<ChatMemberUpdated> {
it.member.id == me.id
}
// This bot updates
onChatMemberJoined(initialFilter = filterSelfUpdates) {
internalLogger.i("Bot was added to chat")
send(it.chat.id, "I was added to chat. Please grant me admin permissions to make me able to watch other users' events")
}
onChatMemberGotPromoted(initialFilter = filterSelfUpdates) {
internalLogger.i("Bot was granted admin permissions")
send(it.chat.id, "I was promoted to admin. I now can watch other users' events")
}
onChatMemberGotDemoted(initialFilter = filterSelfUpdates) {
internalLogger.i("Admin permissions were revoked")
send(it.chat.id, "I'm no longer an admin. Admin permissions are required to watch other users' events")
}
// All users updates
onChatMemberJoined {
val member = it.member
internalLogger.i("${member.firstName} joined the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "Welcome ${member.firstName}")
}
onChatMemberLeft {
val member = it.member
internalLogger.i("${member.firstName} left the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "Goodbye ${member.firstName}")
}
onChatMemberGotPromoted {
val newState = it.newChatMemberState.administratorChatMemberOrThrow()
internalLogger.i("${newState.user.firstName} got promoted to ${newState.customTitle ?: "Admin"}: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "${newState.user.firstName} is now an ${newState.customTitle ?: "Admin"}")
}
onChatMemberGotDemoted {
val member = it.member
internalLogger.i("${member.firstName} got demoted: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "${member.firstName} is now got demoted back to member")
}
onChatMemberGotPromotionChanged {
val member = it.member
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
internalLogger.i(message)
send(it.chat.id, message)
}
onChatMemberUpdated(
initialFilter = chatMemberGotRestrictedFilter + chatMemberGotRestrictionsChangedFilter,
) {
val member = it.member
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
internalLogger.i(message)
send(it.chat.id, message)
}
}.join()
}

View File

@@ -1,41 +1,46 @@
import com.benasher44.uuid.uuid4
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.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.polls.sendQuizPoll
import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollAnswer
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollUpdates
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.customEmojiTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.PollId
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.polls.InputPollOption
import dev.inmo.tgbotapi.types.polls.PollAnswer
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.customEmoji
import dev.inmo.tgbotapi.utils.regular
import dev.inmo.tgbotapi.utils.underline
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.chat.SupergroupChat
import dev.inmo.tgbotapi.types.message.MarkdownV2
import dev.inmo.tgbotapi.types.polls.*
import dev.inmo.tgbotapi.utils.*
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.random.Random
/**
* This bot will answer with anonymous or public poll and send message on
* any update.
* updates of any of it.
*
* * Use `/anonymous` to take anonymous regular poll
* * Use `/public` to take public regular poll
*/
@OptIn(PreviewFeature::class)
suspend fun main(vararg args: String) {
val botToken = args.first()
val isDebug = args.any { it == "debug" }
@@ -49,6 +54,8 @@ suspend fun main(vararg args: String) {
}
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
val pollToChat = mutableMapOf<PollId, IdChatIdentifier>()
val pollToChatMutex = Mutex()

View File

@@ -6,6 +6,7 @@ import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
import dev.inmo.tgbotapi.extensions.api.send.setMessageReactions
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionsCountUpdated
@@ -48,7 +49,6 @@ suspend fun main(vararg args: String) {
when (it) {
is Reaction.CustomEmoji -> regular("") + customEmoji(it.customEmojiId) + regular("(customEmojiId: ${it.customEmojiId})")
is Reaction.Emoji -> regular("${it.emoji}")
is Reaction.Paid -> regular("• Some paid reaction")
is Reaction.Unknown -> regular("• Unknown emoji ($it)")
}
regular("\n")

View File

@@ -1,19 +1,20 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.media.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilterExcludeMediaGroups
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.possiblyWithEffectMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.message.abstracts.BusinessContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.quoteEntitiesField
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.*
suspend fun activateResenderBot(
token: String,
@@ -38,8 +39,7 @@ suspend fun activateResenderBot(
entities = quote ?.textSources ?: emptyList(),
quotePosition = quote ?.position
)
},
effectId = it.possiblyWithEffectMessageOrNull() ?.effectId
}
)
) {
it.forEach(print)

View File

@@ -18,5 +18,5 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation 'io.ktor:ktor-client-logging-jvm:3.0.1'
implementation 'io.ktor:ktor-client-logging-jvm:2.3.7'
}

View File

@@ -16,9 +16,7 @@ import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithFSMAndStartLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitChatSharedEventsMessages
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitCommandMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitUserSharedEventsMessages
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.utils.*
@@ -29,12 +27,12 @@ import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.ChannelChat
import dev.inmo.tgbotapi.types.chat.ChatPermissions
import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.*
import dev.inmo.tgbotapi.utils.mention
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapNotNull
@@ -54,7 +52,6 @@ sealed interface UserRetrievingStep : State {
) : UserRetrievingStep
}
@OptIn(PreviewFeature::class)
suspend fun main(args: Array<String>) {
val botToken = args.first()
@@ -106,7 +103,7 @@ suspend fun main(args: Array<String>) {
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
val chatMember = getChatMember(chatId, userId)
return chatMember.restrictedMemberChatMemberOrNull() ?: chatMember.whenMemberChatMember {
return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember {
getChat(chatId).extendedGroupChatOrNull() ?.permissions
}
}
@@ -227,6 +224,7 @@ suspend fun main(args: Array<String>) {
}
) {
val replyMessage = it.replyTo
val usernameInText = it.content.textSources.firstNotNullOfOrNull { it.mentionTextSourceOrNull() } ?.username
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
if (replyMessage is AccessibleMessage) {
@@ -383,6 +381,7 @@ suspend fun main(args: Array<String>) {
val userId = ChatId(RawChatId(userIdString.toLong()))
val chatMember = getChatMember(channelId, userId)
val asAdmin = chatMember.administratorChatMemberOrNull()
val asMember = chatMember.memberChatMemberOrNull()
val realData = it.data.takeWhile { it != ' ' }
@@ -505,6 +504,7 @@ suspend fun main(args: Array<String>) {
strictlyOn<UserRetrievingStep.RetrievingChatInfoDoneState> { state ->
val chatMember = getChatMember(state.channelId, state.userId).administratorChatMemberOrNull()
if (chatMember == null) {
return@strictlyOn null
}
send(

View File

@@ -1,9 +0,0 @@
# CustomBot
This bot basically have no any useful behaviour, but you may customize it as a playground
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="StarTransactionsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,184 +0,0 @@
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.answers.payments.answerPreCheckoutQueryOk
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getStarTransactions
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.types.RawChatId
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.media.TelegramPaidMediaPhoto
import dev.inmo.tgbotapi.types.media.TelegramPaidMediaVideo
import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaPhoto
import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaVideo
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.payments.LabeledPrice
import dev.inmo.tgbotapi.types.payments.stars.StarTransaction
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.regular
import dev.inmo.tgbotapi.utils.row
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
/**
* An example bot that interacts with Telegram Stars API (used for payments)
*/
suspend fun main(vararg args: String) {
val botToken = args.first()
val adminUserId = args.getOrNull(1) ?.toLongOrNull() ?.let(::RawChatId) ?.let(::UserId) ?: error("Pass user-admin for full access to the bot")
val isDebug = args.any { it == "debug" }
val isTestServer = args.any { it == "testServer" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO), testServer = isTestServer) {
val payload = "sample payload"
command("start") {
reply(
it,
price = LabeledPrice("1", 1L),
title = "Sample",
description = "Sample description",
payload = payload,
replyMarkup = flatInlineKeyboard {
payButton("Pay")
},
)
}
onPreCheckoutQuery(initialFilter = { it.invoicePayload == payload }) {
answerPreCheckoutQueryOk(it)
}
val transactionsDataPrefix = "getStarTransactions"
fun buildTransactionsData(offset: Int, limit: Int = 10) = "$transactionsDataPrefix $offset $limit"
fun parseTransactionsData(data: String): Pair<Int, Int> = data.split(" ").drop(1).let {
it.first().toInt() to it.last().toInt()
}
suspend fun buildStarTransactionsPage(offset: Int, limit: Int = 10): Pair<TextSourcesList, InlineKeyboardMarkup> {
val transactions = getStarTransactions(offset, limit)
return buildEntities {
transactions.transactions.forEach {
regular("Transaction Id: ") + bold(it.id.string) + "\n"
regular("Date: ") + bold(it.date.asDate.toStringDefault()) + "\n"
regular("Amount: ") + bold(it.amount.toString()) + "\n"
when (it) {
is StarTransaction.Incoming -> {
regular("Type: ") + bold("incoming") + "\n"
regular("Partner: ") + bold(it.partner.type) + "\n"
}
is StarTransaction.Outgoing -> {
regular("Type: ") + bold("outgoing") + "\n"
regular("Partner: ") + bold(it.partner.type) + "\n"
}
is StarTransaction.Unknown -> {
regular("Type: ") + bold("unknown") + "\n"
regular("Partner: ") + bold(it.partner.type) + "\n"
}
}
}
} to inlineKeyboard {
row {
val prevOffset = (offset - limit).coerceAtLeast(0)
if (prevOffset < offset) {
dataButton("<", buildTransactionsData(prevOffset, limit))
}
val nextOffset = (offset + limit)
dataButton(">", buildTransactionsData(nextOffset, limit))
}
}
}
onCommand("transactions", initialFilter = { it.sameChat(adminUserId) }) {
val (text, keyboard) = buildStarTransactionsPage(0)
reply(it, text, replyMarkup = keyboard)
}
onMessageDataCallbackQuery(Regex("$transactionsDataPrefix \\d+ \\d+")) {
val (offset, limit) = parseTransactionsData(it.data)
val (text, keyboard) = buildStarTransactionsPage(offset, limit)
edit(
it.message.withContentOrNull<TextContent>() ?: return@onMessageDataCallbackQuery,
text,
replyMarkup = keyboard,
)
}
onVisualGalleryMessages {
send(
it.chat,
1,
it.content.group.mapNotNull {
val file = downloadFileToTemp(it.content.media)
when (it.content.media) {
is VideoFile -> {
TelegramPaidMediaVideo(
file.asMultipartFile()
)
}
is PhotoSize -> {
TelegramPaidMediaPhoto(
file.asMultipartFile()
)
}
else -> null
}
},
it.content.textSources,
showCaptionAboveMedia = true
)
}
onPhoto {
send(
it.chat,
1,
listOf(it.content.media.toTelegramPaidMediaPhoto())
)
}
onVideo {
send(
it.chat,
1,
listOf(it.content.media.toTelegramPaidMediaVideo())
)
}
onPaidMediaInfoContent {
println(it)
}
onRefundedPayment {
reply(
it,
"Received your refund: ${it.chatEvent.payment}"
)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

View File

@@ -1,25 +1,22 @@
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.get.getCustomEmojiStickerOrNull
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
import dev.inmo.tgbotapi.extensions.api.get.getStickerSetOrNull
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.get.*
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.types.StickerFormat
import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.message.textsources.CustomEmojiTextSource
import dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.message.textsources.separateForText
import dev.inmo.tgbotapi.types.message.textsources.*
import dev.inmo.tgbotapi.types.stickers.CustomEmojiStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularStickerSet
import dev.inmo.tgbotapi.types.stickers.StickerSet
import dev.inmo.tgbotapi.types.stickers.UnknownStickerSet
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.*
fun StickerSet?.buildInfo() = buildEntities {
if (this@buildInfo == null) {

View File

@@ -1,6 +1,6 @@
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
import dev.inmo.tgbotapi.extensions.api.send.reply
@@ -10,14 +10,12 @@ import dev.inmo.tgbotapi.extensions.api.stickers.deleteStickerSet
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sticker
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.stickers.InputSticker
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.files.CustomEmojiSticker
import dev.inmo.tgbotapi.types.files.MaskSticker
import dev.inmo.tgbotapi.types.files.RegularSticker
import dev.inmo.tgbotapi.types.files.UnknownSticker
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.toChatId
import dev.inmo.tgbotapi.utils.botCommand
import kotlinx.coroutines.CoroutineScope
@@ -79,18 +77,10 @@ suspend fun main(args: Array<String>) {
runCatchingSafely {
getStickerSet(stickerSetName)
}.onSuccess { stickerSet ->
runCatching {
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
reply(
it,
getStickerSet(stickerSetName).stickers.last()
)
}
}.onFailure { exception ->
exception.printStackTrace()
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
reply(
it,
"Unable to add sticker in stickerset"
getStickerSet(stickerSetName).stickers.last()
)
}
}.onFailure { exception ->
@@ -110,9 +100,5 @@ suspend fun main(args: Array<String>) {
}
}
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
}.second.join()
}

View File

@@ -1,8 +1,18 @@
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.common.repeatOnFailure
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.forum.*
import dev.inmo.tgbotapi.extensions.api.chat.forum.closeForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.closeGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.createForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.deleteForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.editForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.editGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.hideGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.reopenForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.reopenGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.unhideGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand

View File

@@ -6,10 +6,11 @@ import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUsersShared
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.BotCommand

View File

@@ -26,7 +26,6 @@ fun HTMLElement.log(text: String) {
appendElement("p", {})
}
@OptIn(ExperimentalUnsignedTypes::class)
fun main() {
console.log("Web app started")
val client = HttpClient()
@@ -39,7 +38,7 @@ fun main() {
scope.launchSafelyWithoutExceptions {
val response = client.post("$baseUrl/check") {
setBody(
Json.encodeToString(
Json { }.encodeToString(
WebAppDataWrapper.serializer(),
WebAppDataWrapper(webApp.initData, webApp.initDataUnsafe.hash)
)
@@ -182,7 +181,7 @@ fun main() {
val hex = Color.Hex(r, g, b)
webApp.setHeaderColor(hex)
(this as? HTMLButtonElement) ?.style ?.backgroundColor = hex.value
textContent = "Header color: ${webApp.headerColor ?.uppercase()} (click to change)"
textContent = "Header color: ${hex.value.uppercase()} (click to change)"
}
addEventListener("click", {
updateHeaderColor()
@@ -192,38 +191,6 @@ fun main() {
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
fun updateBackgroundColor() {
val (r, g, b) = Random.nextUBytes(3)
val hex = Color.Hex(r, g, b)
webApp.setBackgroundColor(hex)
(this as? HTMLButtonElement) ?.style ?.backgroundColor = hex.value
textContent = "Background color: ${webApp.backgroundColor ?.uppercase()} (click to change)"
}
addEventListener("click", {
updateBackgroundColor()
})
updateBackgroundColor()
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
fun updateBottomBarColor() {
val (r, g, b) = Random.nextUBytes(3)
val hex = Color.Hex(r, g, b)
webApp.setBottomBarColor(hex)
(this as? HTMLButtonElement) ?.style ?.backgroundColor = hex.value
textContent = "Bottom bar color: ${webApp.bottomBarColor ?.uppercase()} (click to change)"
}
addEventListener("click", {
updateBottomBarColor()
})
updateBottomBarColor()
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
fun Element.updateCloudStorageContent() {
clear()
webApp.cloudStorage.getAll {
@@ -319,16 +286,6 @@ fun main() {
}
show()
}
secondaryButton.apply {
setText("Secondary button")
onClick {
document.body ?.log("Secondary button clicked")
hapticFeedback.notificationOccurred(
HapticFeedbackType.Warning
)
}
show()
}
onSettingsButtonClicked {
document.body ?.log("Settings button clicked")
}

View File

@@ -1,20 +1,19 @@
import dev.inmo.kslog.common.*
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.micro_utils.ktor.server.createKtorServer
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUnhandledCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onWriteAccessAllowed
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.webAppButton
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramStartattach
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
@@ -25,21 +24,22 @@ import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.*
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.application.call
import io.ktor.server.http.content.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.request.receiveText
import io.ktor.server.response.respond
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.json.Json
import java.io.File
import java.nio.charset.Charset
/**
* Accepts two parameters:
*
* * Telegram Token
* * URL where will be placed
* * Port (default 8080)
*
* Will start the server to share the static (index.html and WebApp.js) on 0.0.0.0:8080
*/
@@ -58,37 +58,23 @@ suspend fun main(vararg args: String) {
}
)
}
val initiationLogger = KSLog("Initialization")
val bot = telegramBot(telegramBotAPIUrlsKeeper)
createKtorServer(
"0.0.0.0",
args.getOrNull(2) ?.toIntOrNull() ?: 8080
args.getOrNull(2) ?.toIntOrNull() ?: 8080,
additionalEngineEnvironmentConfigurator = {
parentCoroutineContext += Dispatchers.IO
}
) {
routing {
val baseJsFolder = File("WebApp/build/dist/js/")
val prodSubFolder = File(baseJsFolder, "productionExecutable")
val devSubFolder = File(baseJsFolder, "developmentExecutable")
val staticFolder = when {
prodSubFolder.exists() -> {
initiationLogger.i("Folder for static is ${prodSubFolder.absolutePath}")
prodSubFolder
baseJsFolder.list() ?.forEach {
if (it == "productionExecutable" || it == "developmentExecutable") {
staticFiles("", File(baseJsFolder, it)) {
default("WebApp/build/dist/js/$it/index.html")
}
}
devSubFolder.exists() -> {
initiationLogger.i("Folder for static is ${devSubFolder.absolutePath}")
devSubFolder
}
else -> error("""
Unable to detect any folder with static. Current working directory: ${File("").absolutePath}.
Searched paths:
* ${prodSubFolder.absolutePath}
* ${devSubFolder.absolutePath}
""".trimIndent())
}
staticFiles("", staticFolder) {
default("${staticFolder.absolutePath}${File.separator}index.html")
}
post("inline") {
val requestBody = call.receiveText()
@@ -99,7 +85,7 @@ suspend fun main(vararg args: String) {
}
post("check") {
val requestBody = call.receiveText()
val webAppCheckData = Json.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
val webAppCheckData = Json { }.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
@@ -111,6 +97,7 @@ suspend fun main(vararg args: String) {
bot.buildBehaviourWithLongPolling(
defaultExceptionsHandler = { it.printStackTrace() }
) {
val me = getMe()
onCommand("reply_markup") {
reply(
it,

View File

@@ -1,28 +0,0 @@
# WebHooks
Launches webhook-based simple bot. Use `/start` with bot to get simple info about webhooks
## Launch
```bash
../gradlew run --args="BOT_TOKEN https://sample.com it/is/subpath 8080 debug"
```
Required arguments:
1. Token
2. Arguments starting with `https://`
Optional arguments:
* Any argument == `debug` to enable debug mode
* Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
subroute to place listening of webhooks)
* Any argument as number of port
Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
* `TOKEN` used as token
* Bot will set up its webhook info as `https://sample.com/it/is/subpath`
* Bot will set up to listen webhooks on route `it/is/subpath`
* Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`

View File

@@ -1,23 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="WebHooksKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
implementation "io.ktor:ktor-server-cio:$ktor_version"
}

View File

@@ -1,87 +0,0 @@
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.ktor.server.createKtorServer
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.webhook.setWebhookInfo
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.includeWebhookHandlingInRoute
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.utils.buildEntities
import io.ktor.server.routing.*
/**
* Launches webhook-based simple bot. Required arguments:
*
* 1. Token
* *. Arguments starting with `https://`
*
* Optional arguments:
*
* *. Any argument == `debug` to enable debug mode
* *. Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
* subroute to place listening of webhooks)
* *. Any argument as number of port
*
* Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
*
* * `TOKEN` used as token
* * Bot will set up its webhook info as `https://sample.com/it/is/subpath`
* * Bot will set up to listen webhooks on route `it/is/subpath`
* * Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`
*/
suspend fun main(args: Array<String>) {
val botToken = args.first()
val address = args.first { it.startsWith("https://") }
val subpath = args.drop(1).firstOrNull { it != address && it != "debug" }
val port = args.firstNotNullOfOrNull { it.toIntOrNull() } ?: 8080
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val bot = telegramBot(botToken)
val behaviourContext = bot.buildBehaviour (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommand("start", initialFilter = { it.chat is PrivateChat }) {
reply(
it,
buildEntities {
+"Url: $address" + "\n"
+"Listening server: 0.0.0.0" + "\n"
+"Listening port: $port"
}
)
}
setMyCommands(BotCommand("start", "Get webhook info"))
}
val webhookInfoSubpath = subpath ?.let { "/" + it.removePrefix("/") } ?: "" // drop leading `/` to add it in the beginning for correct construction of subpath
bot.setWebhookInfo(address + webhookInfoSubpath)
createKtorServer(
"0.0.0.0",
port,
) {
routing {
if (subpath == null) {
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
} else {
route(subpath) {
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
}
}
}
}.start(true)
}

View File

@@ -1,12 +1,11 @@
kotlin.code.style=official
org.gradle.parallel=true
# Due to parallel compilation project require next amount of memory on full build
org.gradle.jvmargs=-Xmx3148m
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
org.gradle.jvmargs=-Xmx2344m
kotlin_version=2.0.21
telegram_bot_api_version=20.1.0
micro_utils_version=0.23.0
serialization_version=1.7.3
ktor_version=3.0.1
kotlin_version=1.9.23
telegram_bot_api_version=13.0.0
micro_utils_version=0.20.45
serialization_version=1.6.3
ktor_version=2.3.10

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip

309
gradlew vendored
View File

@@ -1,78 +1,127 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# 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
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
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=""
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# 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
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | 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"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,92 +130,120 @@ 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.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
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
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" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
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, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# 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"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# 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")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

76
gradlew.bat vendored
View File

@@ -1,4 +1,20 @@
@if "%DEBUG%" == "" @echo off
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -9,25 +25,29 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@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
if %ERRORLEVEL% equ 0 goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -35,48 +55,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
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.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
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%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 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
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View File

@@ -50,12 +50,4 @@ include ":BoostsInfoBot"
include ":BusinessConnectionsBot"
include ":StarTransactionsBot"
include ":GiveawaysBot"
include ":CustomBot"
include ":MemberUpdatedWatcherBot"
include ":WebHooks"