mkdocs inited

This commit is contained in:
InsanusMokrassar 2023-06-10 01:50:56 +06:00
parent 43f8fcaf6e
commit b08ddfea84
25 changed files with 1563 additions and 0 deletions

17
.fleet/run.json Normal file
View File

@ -0,0 +1,17 @@
{
"configurations": [
{
"type": "command",
"name": "Rebuild",
"program": "mkdocs",
"args": ["build"],
},
{
"type": "command",
"name": "Serve",
"program": "mkdocs",
"args": ["serve"],
},
]
}

27
.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Build and publish
on:
push:
branch:
- master
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mkdocs markdown-callouts mkdocs-autorefs mkdocs-include-dir-to-nav mkdocs-material
- name: Build
run: mkdocs build
- name: Publish
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
publish_branch: site

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
site/

7
docs/index.md Normal file
View File

@ -0,0 +1,7 @@
# Insanus Mokrassar libraries home
Hello :) It is my libraries docs place and I glad to welcome you here. I hope, this documentation place will help you.
## Projects
* [TelegramBotAPI](https://github.com/InsanusMokrassar/ktgbotapi)

View File

@ -0,0 +1,64 @@
# Keyboards
In the telegram system there are two types of keyboards:
| Reply | Inline |
|:-:|:-:|
| [![Reply keyboard](https://core.telegram.org/file/811140880/1/jS-YSVkDCNQ/b397dfcefc6da0dc70)](https://core.telegram.org/bots#keyboards) | [![Inline keyboard](https://core.telegram.org/file/811140659/1/RRJyulbtLBY/ea6163411c7eb4f4dc)](https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating) |
| Keyboard for each user in the chat | Keyboard linked to the certain message |
Low-level way to create keyboard looks like in the next snippet:
```kotlin
ReplyKeyboardMarkup(
matrix {
row {
add(SimpleKeyboardButton("Simple text"))
// ...
}
// ...
}
)
```
In case you wish to create inline keyboard, it will look like the same as for reply keyboard. But there is another way. The next snippet will create the same keyboard as on the screenshots above:
```kotlin
// reply keyboard
replyKeyboard {
row {
simpleButton("7")
simpleButton("8")
simpleButton("9")
simpleButton("*")
}
row {
simpleButton("4")
simpleButton("5")
simpleButton("6")
simpleButton("/")
}
row {
simpleButton("1")
simpleButton("2")
simpleButton("3")
simpleButton("-")
}
row {
simpleButton("0")
simpleButton(".")
simpleButton("=")
simpleButton("+")
}
}
// inline keyboard
inlineKeyboard {
row {
dataButton("Get random music", "random")
}
row {
urlButton("Send music to friends", "https://some.link")
}
}
```

View File

@ -0,0 +1,76 @@
# Live Location
Bot API allows you to send live locations and update them during their lifetime. In this library there are several ways to use this API:
* Directly via API calls ([sendLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.send/send-live-location.html) and [editLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.edit.location.live/edit-live-location.html))
* [startLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/start-live-location.html)
* [handleLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/handle-live-location.html)
## sendLiveLocation
In the Bot API there is no independent `sendLiveLocation` method, instead it is suggested to use [sendLocation](https://core.telegram.org/bots/api#sendlocation) with setting up `live_period`. In this library in difference with original Bot API live location is special request. It was required because of in fact live locations and static locations are different types of location info and you as bot developer may interact with them differently.
Anyway, in common case the logic looks like:
* Send [sendLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.send/send-live-location.html)
* Use [editLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.edit.location.live/edit-live-location.html) to change it during its lifetime
* Use [stopLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.edit.location.live/stop-live-location.html) to abort it before lifetime end
## startLiveLocation
In difference with [sendLiveLocation](#sendLiveLocation), [startLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/start-live-location.html) using [LiveLocationProvider](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-live-location-provider/index.html). With this provider you __need not__ to handle chat and message ids and keep some other data for location changes. Instead, you workflow with provider will be next:
* [startLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/start-live-location.html)
* Use [LiveLocationProvider#updateLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-live-location-provider/update-location.html) to update location and optionally add [inline keyboard](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.buttons/-inline-keyboard-markup/index.html)
* Use [LiveLocationProvider#close](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-live-location-provider/close.html) to abort live location before its end
Besides, `LiveLocationProvider` [contains different useful parameters](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-live-location-provider/index.html#141178175%2FProperties) about live location
## handleLiveLocation
This way of live locations handling is based on coroutines [Flow](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/) and allow you to pass some external `Flow` with [EditLiveLocationInfo](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-edit-live-location-info/index.html). So, workflow:
* Create your own flow of locations. For example:
```kotlin
flow {
var i = 0
while (isActive) {
val newInfo = EditLiveLocationInfo(
latitude = i.toDouble(),
longitude = i.toDouble(),
replyMarkup = flatInlineKeyboard {
dataButton("Cancel", "cancel")
}
)
emit(newInfo)
i++
delay(10000L) // 10 seconds
}
}
```
* In case you needed, create your collector to store the message with live location:
```kotlin
val currentMessageState = MutableStateFlow<ContentMessage<LocationContent>?>(null)
```
* Start handle live location. [handleLiveLocation](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/handle-live-location.html) works synchronosly (in current coroutine) and will ends only when your flow will ends. Thats why there are two ways to call it:
```kotlin
handleLiveLocation(
it.chat.id,
locationsFlow,
sentMessageFlow = FlowCollector { currentMessageState.emit(it) }
)
// this code will be called after `locationsFlow` will ends
```
OR
```kotlin
scope.launch {
handleLiveLocation(
it.chat.id,
locationsFlow,
sentMessageFlow = FlowCollector { currentMessageState.emit(it) }
)
}
// this code will be called right after launch will be completed
```
See our [example](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/blob/master/LiveLocationsBot/src/main/kotlin/LiveLocationsBot.kt) to get more detailed sample

View File

@ -0,0 +1,25 @@
# Text
For the text creating there are several tools. The most simple one is to concatenate several text sources to make list of text sources as a result:
```kotlin
val sources = "Regular start of text " + bold("with bold part") + italic("and italic ending")
```
But there is a little bit more useful way: entities builder:
```kotlin
val items = (0 until 10).map { it.toString() }
buildEntities(" ") {// optional " " auto separator which will be pasted between text sources
+"It is regular start too" + bold("it is bold as well")
items.forEachIndexed { i, item ->
if (i % 2) {
italic(item)
} else {
strikethrough(item)
}
}
}
```
In the code above we are creating an items list just for demonstrating that inside of buildEntities body we may use any operations for cunstructing our result list of `TextSource`s. As a result, will be created the list which will looks like in telegram as "It is regular start too **it is bold as well** *0* ~~1~~ *2* ~~3~~ *4* ~~5~~ *6* ~~7~~ *8* ~~9~~".

View File

@ -0,0 +1,12 @@
# Before any bot project
There are several places you need to visit for starting work with any Telegram Bot framework on any language:
* [Bots info introduction](https://core.telegram.org/bots)
* [Telegram Bot API reference](https://core.telegram.org/bots/api) (you can skip it, but it could be useful to know some specific cases in Telegram Bot API)
Anyway, the most important link is [How do I create a bot?](https://core.telegram.org/bots#3-how-do-i-create-a-bot) inside of Telegram Bot API
## Next steps
* [Including in your project](including-in-your-project.html)

View File

@ -0,0 +1,30 @@
# First bot
> NOTE: **Examples info**
> A lot of examples with using of Telegram Bot API you can find in [this github repository](https://github.com/InsanusMokrassar/TelegramBotAPI-examples)
### The most simple bot
The most simple bot will just print information about itself. All source code you can find [in this repository](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/tree/master/GetMeBot). Our interest here will be concentrated on the next example part:
```kotlin
suspend fun main(vararg args: String) {
val botToken = args.first()
val bot = telegramBot(botToken)
println(bot.getMe())
}
```
So, let's get understanding, about what is going on:
1. `suspend fun main(vararg args: String)`:
* `suspend` required for making of requests inside of this function. For more info you can [open official documentation](https://kotlinlang.org/docs/reference/coroutines/basics.html) for coroutins. In fact, `suspend fun main` is the same that `fun main() = runBlocking {}` from examples
2. `val botToken = args.first()`: here we are just getting the bot token from first arguments of command line
3. `val bot = telegramBot(botToken)` : inside of `bot` will be [RequestsExecutor](https://tgbotapi.inmo.dev/docs/com.github.insanusmokrassar.-telegram-bot-a-p-i.bot/-requests-executor/index.html) object which will be used for all requests in any project with this library
4. `println(bot.getMe())`: here happens calling of [getMe](https://tgbotapi.inmo.dev/docs/com.github.insanusmokrassar.-telegram-bot-a-p-i.extensions.api.bot/get-me.html) extension
As a result, we will see in the command line something like
```shell
ExtendedBot(id=ChatId(chatId=123456789), username=Username(username=@first_test_ee17e8_bot), firstName=Your bot name, lastName=, canJoinGroups=false, canReadAllGroupMessages=false, supportsInlineQueries=false)
```

View File

@ -0,0 +1,154 @@
# Including in your project
There are three projects:
* `TelegramBotAPI Core` - project with base for all working with Telegram Bot API
* `TelegramBotAPI API Extensions` - extension of `TelegramBotAPI` with functions for more comfortable work with Telegram Bot API
* `TelegramBotAPI Utils Extensions` - extension of `TelegramBotAPI` with functions for extending of different things like retrieving of updates
> NOTE: **TelegramBotAPI**
>
> Also, there is an aggregator-version `tgbotapi`, which will automatically include all projects above. It is most recommended version due to the fact that it is including all necessary tools around `TelegramBotAPI Core`, but it is optionally due to the possible restrictions on the result methods count (for android) or bundle size
> NOTE: **Examples**
>
> You can find full examples info in [this repository](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/). In this repository there full codes which are working in normal situation. Currently, there is only one exception when these examples could work incorrectly: you are living in the location where Telegram Bot API is unavailable. For solving this problem you can read [Proxy setup](proxy-setup) part
### Notice about repository
To use this library, you will need to include `Maven Central` repository in your project
###### build.gradle
```groovy
mavenCentral()
```
###### pom.xml
```xml
<repository>
<id>central</id>
<name>mavenCentral</name>
<url>https://repo1.maven.org/maven2</url>
</repository>
```
###### Dev channel
Besides, there is [developer versions repo](https://git.inmo.dev/InsanusMokrassar/-/packages/maven/dev.inmo-tgbotapi). To use it in your project, add the repo in `repositories` section:
<details><summary>Gradle</summary>
```groovy
maven {
url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven"
}
```
</details>
<details><summary>Maven</summary>
```xml
<repository>
<id>dev.inmo</id>
<name>InmoDev</name>
<url>https://git.inmo.dev/api/packages/InsanusMokrassar/maven</url>
</repository>
```
</details>
### TelegramBotAPI
As `tgbotapi_version` variable in next snippets will be used variable with next last published version:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi)
###### build.gradle
```groovy
implementation "dev.inmo:tgbotapi:$tgbotapi_version"
```
###### pom.xml
```xml
<dependency>
<groupId>dev.inmo</groupId>
<artifactId>tgbotapi</artifactId>
<version>${tgbotapi_version}</version>
</dependency>
```
### TelegramBotAPI Core
As `tgbotapi_version` variable in next snippets will be used variable with next last published version:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.core)
###### build.gradle
```groovy
implementation "dev.inmo:tgbotapi.core:$tgbotapi_version"
```
###### pom.xml
```xml
<dependency>
<groupId>dev.inmo</groupId>
<artifactId>tgbotapi.core</artifactId>
<version>${tgbotapi_version}</version>
</dependency>
```
### TelegramBotAPI API Extensions
As `tgbotapi_version` variable in next snippets will be used variable with next last published version:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.api)
###### build.gradle
```groovy
implementation "dev.inmo:tgbotapi.api:$tgbotapi_version"
```
###### pom.xml
```xml
<dependency>
<groupId>dev.inmo</groupId>
<artifactId>tgbotapi.api</artifactId>
<version>${tgbotapi_version}</version>
</dependency>
```
### TelegramBotAPI Utils Extensions
As `tgbotapi_version` variable in next snippets will be used variable with next last published version:
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.utils/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.utils)
###### build.gradle
```groovy
implementation "dev.inmo:tgbotapi.utils:$tgbotapi_version"
```
###### pom.xml
```xml
<dependency>
<groupId>dev.inmo</groupId>
<artifactId>tgbotapi.utils</artifactId>
<version>${tgbotapi_version}</version>
</dependency>
```
## Next steps
* [Proxy setup](proxy-setup.html)
* [First bot](first-bot.html)

View File

@ -0,0 +1,59 @@
# Proxy setup
In some locations Telegram Bots API urls will be unavailable. In this case all examples will just throw exception like:
```bash
Exception in thread "main" java.net.ConnectException: Connection refused
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
at io.ktor.network.sockets.SocketImpl.connect$ktor_network(SocketImpl.kt:36)
at io.ktor.network.sockets.SocketImpl$connect$1.invokeSuspend(SocketImpl.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Process finished with exit code 1
```
There are several ways to solve this problem:
* Built-in proxy config (will require some socks or http proxy server)
* System-configured VPN or proxy
* Your own Bot API Server
### Using Ktor Client built-in proxy
First of all, you will need to use one more library:
**build.gradle**:
```groovy
implementation "io.ktor:ktor-client-okhttp:2.0.1"
```
> NOTE: **Dependency note**
> In the snippet above was used version `2.0.1` which is actual for `TelegramBotAPI` at the moment of filling this documentation (`May 22 2022`, `TelegramBotAPI` version `2.0.0`) and you can update version of this dependency in case if it is outdated.
For configuring proxy for your bot inside your program, you can use next snippet:
```kotlin
val botToken = "HERE MUST BE YOUR TOKEN"
val bot = telegramBot(botToken) {
ktorClientEngineFactory = OkHttp
proxy = ProxyBuilder.socks("127.0.0.1", 1080)
}
```
Explanation line by line:
1. `val botToken = "HERE MUST BE YOUR TOKEN"` - here we are just creating variable `botToken`
2. `val bot = telegramBot(botToken) {` - start creating bot
3. `ktorClientEngineFactory = OkHttp` - setting up engine factory of our bot. On the time of documentation filling, `OkHttp` is one of the engines in `Ktor` system which supports socks proxy. More you can read on [Ktor](https://ktor.io) site in subparts about [engines](https://ktor.io/clients/http-client/engines.html#okhttp) and [proxy](https://ktor.io/clients/http-client/features/proxy.html)
4. `proxy = ProxyBuilder.socks("127.0.0.1", 1080)` - here we are setting up our proxy. Here was used local server which (as assumed) will connect to server like `shadowsocks`
## Next steps
* [First bot](first-bot.html)

View File

@ -0,0 +1,37 @@
# API Extensions
[API extensions](https://github.com/InsanusMokrassar/TelegramBotAPI/tree/master/tgbotapi.api) is a module which you may include in your project in addition to [core part](https://github.com/InsanusMokrassar/TelegramBotAPI/tree/master/tgbotapi.core). In most cases this module will allow just use syntax like `bot.getUpdates()` instead of `bot.execute(GetUpdates())`, but there are several other things you will achieve with that syntax.
## Bot builder
This functionality allow you to build bot in more unified and comfortable way than standard creating with `telegramBot` function
```kotlin
buildBot(
"TOKEN"
) {
proxy = ProxyBuilder.socks(host = "127.0.0.1", port = 4001) // just an example, more info on https://ktor.io/docs/proxy.html
ktorClientConfig = {
// configuring of ktor client
}
ktorClientEngineFactory = {
// configuring of ktor client engine
}
}
```
## Downloading of files
In standard library requests there are no way to download some file retrieved in updates or after requests. You may use syntax like `bot.downloadFile(file)` where `file` is `TelegramMediaFile` from telegram, `FileId` or even `PathedFile` from [GetFile](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/get/GetFile.kt) request ([sources](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFile.kt)).
## Live location
By default, you should handle updates of Live location by your code. But with extension [bot#startLiveLocation](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/LiveLocationProvider.kt#L84) you may provide all necessary startup parameters and handle updates with just calling `updateLocation` for retrieved [LiveLocationProvider](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api/-live-location-provider/index.html).
## What is next?
There are several things you may read next:
* [Updates retrieving](https://bookstack.inmo.dev/books/telegrambotapi/chapter/updates-retrieving)
* Read about [second level](https://bookstack.inmo.dev/books/telegrambotapi/page/updates-with-flows) of working with library
* Read about [BehaviourBuilder](https://bookstack.inmo.dev/books/telegrambotapi/page/behaviour-builder)

View File

@ -0,0 +1,57 @@
# Behaviour Builder with FSM
Behaviour builder with FSM is based on the MicroUtils FSM. There are several important things in FSM:
* `State` - any object which implements [State](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common/-state/index.html) interface
* `StateHandler` (or [CheckableHandlerHolder](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common/-checkable-handler-holder/index.html)) - the handler of states
* [StatesMachine](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common/-states-machine/index.html) - some machine which work with states and handlers
* [StatesManager](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common/-states-manager/index.html) - simple manager that will solve which states to save and notify about states changes via its flows
`StatesMachine` have two methods:
* `start` which will start work of machine
* `startChain` which will add new state for handling
The most based way to create `StatesMachine` and register `StateHandler`s looks like in the next snippet:
```kotlin
buildFSM<TrafficLightState> {
strictlyOn<SomeState> {
// state handling
}
}.start(CoroutineScope(...)).join()
```
> NOTE: **Full example**
> You may find full example of FSM usage in [the tests of FSM in MicroUtils](https://github.com/InsanusMokrassar/MicroUtils/blob/master/fsm/common/src/jvmTest/kotlin/PlayableMain.kt)
So, you must do next steps before you will launch your bot with FSM:
* Create your states. Remember that you may plan to save them, so it is likely you will need to serialize it there
* Create your handlers for your states. In most cases it is useful to use [CheckableHandlerHolder](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common/-checkable-handler-holder/index.html) if you want to use standard states machine
* Solve which states managers to use (the most simple one is the [DefaultStatesManager](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common.managers/-default-states-manager/index.html) with [InMemoryDefaultStatesManager](https://microutils.inmo.dev/micro_utils.dokka/dev.inmo.micro_utils.fsm.common.managers/-in-memory-default-states-manager-repo/index.html))
## Bot with FSM
There are several extensions for `TelegramBot` to create your bot with FSM:
* [buildBehaviourWithFSM](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/build-behaviour-with-f-s-m.html)
* [buildBehaviourWithFSMAndStartLongPolling](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/build-behaviour-with-f-s-m-and-start-long-polling.html)
* [telegramBotWithBehaviourAndFSM](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/telegram-bot-with-behaviour-and-f-s-m.html)
* [telegramBotWithBehaviourAndFSMAndStartLongPolling
](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/telegram-bot-with-behaviour-and-f-s-m-and-start-long-polling.html)
All of them will take as an callback some object with type [CustomBehaviourContextReceiver](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/index.html#-1892390839%2FClasslikes%2F-1982836883) and will looks like in the next snippet:
```kotlin
telegramBotWithBehaviourAndFSMAndStartLongPolling<YourStateType>("BOT_TOKEN") {
// here you may use any operations from BehaviourBuilder
// here you may use any operations from BehaviourContextWithFSMBuilder like strictlyOn and others
}
```
## Examples
* [TelegramBotAPI-examples/FSMBot](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/blob/master/FSMBot/src/main/kotlin/SimpleFSMBot.kt)
* [MicroUtils simple example in the tests](https://github.com/InsanusMokrassar/MicroUtils/blob/master/fsm/common/src/jvmTest/kotlin/PlayableMain.kt)

View File

@ -0,0 +1,68 @@
# Behaviour Builder
In the previous pages about [updates handling](https://bookstack.inmo.dev/books/telegrambotapi/page/updates-with-flows) and was mentioned that currently in the most cases you should use [Flow](https://kotlinlang.org/docs/flow.html)s. So, there is an improvement for that system which hide direct work with flows and allow you to create more declarative logic of your bot.
## Main parts of Behaviour Builder
There are several things you should know for better understanding of behaviour builder:
* [BehaviourContext](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.behaviour_builder/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContext.kt#L33) - it is the thing which contains all necessary tools for working with bots
* [Triggers](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling/index.html) - `on*` extensions for `BehaviourContext` which allow you to create reaction on some update
* [Expectations](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder.expectations/index.html) (or waiters) - `wait*` extensions which you **may** use in [buildBehaviour](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/build-behaviour.html) function, but ***it is recommended*** to use it in bodies of triggers
## Initialization
As was said above, there is [buildBehaviour](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/build-behaviour.html) function which allow you set up your bot logic. Let's see an example:
```kotlin
val bot = telegramBot("TOKEN")
bot.buildBehaviour {
onCommand("start") { // creating of trigger
val message = it
val content = message.content
reply(message, "Ok, send me one photo") // send text message with replying on incoming message
val photoContent = waitPhoto().first() // waitPhoto will return List, so, get first element
val photo = downloadFile(photoContent) // ByteArray of photo
// some logic with saving of photos
}
}
```
## Filters
In most cases there are opportunity to filter some of messages before starting of main logic. Let's look at this using the example above:
```kotlin
val bot = telegramBot("TOKEN")
bot.buildBehaviour {
onCommand(
"start",
initialFilter = {
it.content.textSources.size == 1 // make sure that user has sent /start without any additions
}
) {
// ...
}
}
```
OR
```kotlin
val bot = telegramBot("TOKEN")
bot.buildBehaviour {
onCommand(
"start",
requireOnlyCommandInMessage = true // it is default, but you can overwrite it with `requireOnlyCommandInMessage = false`
) {
// ...
}
}
```

View File

@ -0,0 +1,97 @@
# Exceptions handling
Unfortunatelly, exceptions handling in this library is a bit difficult in some places, but that have at least two reasons: flexibility and usability.
## "In place" handling
In case you know, where exceptions are happening, you may use several tools for exceptions catching:
* Catching with result
* Catching with callback
### Catching with result
If you prefer to receive `Result` objects instead of some weird callbacks, you may use the next syntax:
```kotlin
safelyWithResult {
// do something
}.onSuccess { // will be called if everything is right
// handle success
}.onFailure { // will be called if something went wrong
// handle error
it.printStackTrace()
}.getOrThrow() // will return value or throw exception
```
### Catching with callback
Also there is more simple (in some cases) way to handle exceptions with callbacks:
```kotlin
safely(
{
// handle error
it.printStackTrace()
null // return value
}
) {
// do something
}
```
### Bonus: different types of handling
There are two types of handling:
* Just safely - when you are using something to obviously retrieve value or throw exception. When handling callback has been skipped, it will throw exception by default. For example:
```kotlin
safely(
{
it.printStackTrace()
"error"
}
) {
error("Hi :)") // emulate exception throwing
"ok"
} // result will be with type String
```
* Safely without exceptions - almost the same as `safely`, but this type by default allow to return nullable value (when exception was thrown) instead of just throwing (as with `safely`):
```kotlin
safelyWithouExceptions {
// do something
} // will returns nullable result type
```
## Global exceptions handling
The most simple way to configure exceptions handling is to change `CoroutineContext` when you are creating your `CoroutineScope` for bot processing:
```kotlin
val bot = telegramBot("TOKEN")
bot.buildBehaviour (
scope = scope,
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
// ...
}
```
OR
```kotlin
val bot = telegramBotWithBehaviour (
"TOKEN",
scope = scope,
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
// ...
}
```
Here we have used `ContextSafelyExceptionHandler` class. It will pass default handling of exceptions and will call the block in most cases when something inside of your bot logic has thrown exception.

View File

@ -0,0 +1,162 @@
# Files handling
According to the [documentation](https://core.telegram.org/bots/api#sending-files) there are several ways to work with files:
* By [FileId](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt#L52)
* By [FileUrl](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt#L56) (`typealias` for the `FileId`)
* By some [MultipartFile](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt#L74) (in Telegram Bot API it is multipart requests)
# Files receiving
There are several cases you may need in your app to work with files:
* Save `FileId` (for sending in future)
* Download some file into memory/file in filesystem
### Where to get File id or url?
The most simple way to send some file is to get file id and send it. You may get file id from any message with media. For example, if you have received some [Message](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/abstracts/Message.kt#L12), you may use [asCommonMessage](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/ClassCasts.kt#L1094) conversation to be able to get its `content` and then convert it to some content with media. Full code here:
```kotlin
val message: Message;
val fileId = message.asCommonMessage() ?.withContent<MediaContent>() ?.content ?.media ?.fileId;
```
**WAT? O.o**
In the code above we get some message, safely converted it to `CommonMessage` with `asCommonMessage`, then safely took its content via `withContent<MediaContent>() ?.content` and then just get its media file id.
## Download files
There are three ways to download files:
* Download it in memory as `ByteArray`
* Take `ByteReadChannelAllocator` which allow to retrieve [ByteReadChannel](https://api.ktor.io/ktor-io/io.ktor.utils.io/-byte-read-channel/index.html) and do whatever you want with it
* `[JVM Only]` Download it directly to file or temporal file
### Downloading with `API` extensions
#### Files (JVM/Android)
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val outputFile: File;
bot.downloadFile(fileId, outputFile)
```
See [downloadFile](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.files/download-file.html) extension docs in the __JVM tab__ to get more available options
There is also way with saving of data into temporal file. That will allow you to do with data whatever you want without high requirements to memory or network connection:
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val tempFile: File = bot.downloadFileToTemp(fileId)
```
See [downloadFileToTemp](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.files/download-file-to-temp.html) extension docs to get more available options
#### Byte read channel
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val bytes: ByteReadChannelAllocator = bot.downloadFileStream(fileId)
```
See [downloadFileStream](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.files/download-file-stream.html) extension docs to get more available options
#### Byte read channel allocator
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val bytes: ByteReadChannelAllocator = bot.downloadFileStreamAllocator(fileId)
```
See [downloadFileStreamAllocator](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.files/download-file-stream-allocator.html) extension docs to get more available options
#### Byte arrays
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val bytes: ByteArray = bot.downloadFile(fileId)
```
See [downloadFile](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.api.files/download-file.html) extension docs to get more available options
### Low level or `how does it work?`
You may download file with streams or with downloading into the memory first. On low level you should do several things. They are presented in next snippet:
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val pathedFile: PathedFile = bot.execute(GetFile(fileId))
val downloadedBytes: ByteArray = bot.execute(DownloadFile(pathedFile.filePath))
```
In the snippet above we are getting file `PathedFile` by its `FileId` and use it to download file bytes into memory using `DownloadFile` request.
You may use almost the same way but with byte read channel allocator:
```kotlin
val bot: TelegramBot;
val fileId: FileId;
val pathedFile: PathedFile = bot.execute(GetFile(fileId))
val channelAllocator: ByteReadChannelAllocator = bot.execute(DownloadFileStream(pathedFile.filePath))
val byteReadChannel: ByteReadChannel = channelAllocator()
```
And then you may look into [ByteReadChannel](https://api.ktor.io/ktor-io/io.ktor.utils.io/-byte-read-channel/index.html) docs to get more info about what you can do with that.
> NOTE: **Several useful links**
>
> * [GetFile]("https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.requests.get/-get-file/index.html")
> * [PathedFile]("https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.files/-pathed-file/index.html")
> * [DownloadFile]("https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.requests/-download-file/index.html")
> * [DownloadFileStream]("https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.requests/-download-file-stream/index.html")
# Files sending
Of course, in most cases you must be sure that file have correct type.
## FileId and FileUrl
It is the most simple way to send any media in Telegram, but this way have several restrictions:
* The `FileId` which has retrieved for file should not (and probably will not too) equal to the `FileId` retrieved by some other bot
* There is a chance that the file id you are using will be expired with time
## Sending via file
> WARNING: **JS Restrictions**
> Sending via file is accessible from all supported platforms, but there is small note about `JS` - due to restrictions of work with streams and stream-like data (`JS` have no native support of files streaming) on this platform all the files will be loaded inside of RAM before the sending to the telegram services.
Sending via file is available throw the [MultipartFile](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.requests.abstracts/-multipart-file/index.html). There are several wayt to get it:
* Simple creating via its constructor: `MultipartFile("filename.jpg") { /* here Input allocation */ }`
* Via [asMultiparFile](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.requests.abstracts/as-multipart-file.html) extension applicable to any `ByteArray`, `ByteReadChannel`, `ByteReadChannelAllocator` or `File` (on any platform)
In most cases, sending via files looks like in the next snippet:
```kotlin
val file: File;
bot.sendDocument(chatId, file.asMultipartFile())
```

View File

@ -0,0 +1,27 @@
# Low-level work with bots
The base version of library was done a lot of time ago and just got several additions related to improvements, updates in [Telegram Bot API](https://core.telegram.org/bots/api) or some requests from [our community](https://t.me/InMoTelegramBotAPIChat).
# Base things
There are several important things in context of this library:
* [RequestsExecutor](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/RequestsExecutor.kt#L13) (also "known" as `TelegramBot`)
* [Types](https://github.com/InsanusMokrassar/TelegramBotAPI/tree/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types)
* [Requests](https://github.com/InsanusMokrassar/TelegramBotAPI/tree/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests)
So, in most cases all your request calls with simplified api of this library (like `bot.getMe()`) will looks like `bot.execute(GetMe)`. Result of these calls is defined in type of any request (for example, for [GetMe](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/bot/GetMe.kt) request the result type is [ExtendedBot](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/chat/Extended.kt#L101)). As a result, you can avoid any extension api (like [special API extensions](api-extensions.html)) and use low level request with full controlling of the whole logic flow.
## How to handle updates
As was written above, it will require some request:
```kotlin
val updates = bot.execute(GetUpdates())
```
Result type of [GetUpdates](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/GetUpdates.kt#L24) request is [Update](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/update/abstracts/Update.kt#L13). You may find _inheritors_ of this interface in [Update kdocs](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.update.abstracts/-update/index.html).
## What is next?
As was said above, you may look into our [API extensions](api-extensions.html) in case you wish to use more high-level functions instead of `bot.execute(SomeRequest())`. Besides, it will be very useful to know more about [updates retrieving](../updates).

View File

@ -0,0 +1,45 @@
# Media Groups
As you know, Telegram have the feature named Media Groups. Media groups have several differences with the common messages:
* Each media group message contains special media group id
* Media group may have special caption which will be visible if only the first message of media group contains caption
* In most cases media groups came with long polling/webhooks in one pack
* Media groups can be one of three types:
* Visual (image/video)
* Documents
* Playlists (audio)
## Specific of media groups in libraries
> NOTE: **Row updates**
> In tgbotapi there is no any additional handling of media groups by default and in case you will use simple [bot.getUpdates](https://insanusmokrassar.github.io/TelegramBotAPI/docs/dev.inmo.tgbotapi.extensions.api/get-updates.html), you will get the list of row updates and media groups will be included in this list as separated messages with [MediaGroupPartContent](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.message.content/-media-group-part-content/index.html). In that case you may use [convertWithMediaGroupUpdates](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.utils.updates/convert-with-media-group-updates.html) to be able to work with media groups as will be described below
In case you are using standard [long polling](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.utils.updates.retrieving/long-polling.html) (one of alternatives is [telegramBotWithBehaviourAndLongPolling](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder/telegram-bot-with-behaviour-and-long-polling.html)) or [webhooks](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.utils.updates.retrieving/set-webhook-info-and-start-listen-webhooks.html) updates will be converted uner the hood and as a result, you will take media groups as a content in one message:
```kotlin
telegramBotWithBehaviourAndLongPolling(
"token"
) {
onVisualGallery { // it: CommonMessage<MediaGroupContent<VisualMediaGroupPartContent>>
it.content // MediaGroupContent<VisualMediaGroupPartContent>
it.content.group // List<MediaGroupCollectionContent.PartWrapper<VisualMediaGroupPartContent>>
it.content.group.forEach { // it: MediaGroupCollectionContent.PartWrapper<VisualMediaGroupPartContent>
it.messageId // source message id for current media group part
it.sourceMessage // source message for current media group part
it.content // VisualMediaGroupPartContent
println(it.content) // will print current content part info
}
}
}
```
**KDocs:**
* [onVisualGallery](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling/on-visual-gallery-messages.html)
* [MediaGroupContent](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.message.content/-media-group-content/index.html)
* [VisualMediaGroupPartContent](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.message.content/-visual-media-group-part-content/index.html)
* [MediaGroupCollectionContent.PartWrapper](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.types.message.content/-media-group-collection-content/-part-wrapper/index.html)
In two words, in difference with row Telegram Bot API, you will take media groups in ___one message___ instead of ___messages list___.

View File

@ -0,0 +1,40 @@
# Types conversations
One of the most important topics in context of tgbotapi is types conversations. This library is very strong-typed and a lot of things are based on types hierarchy. Lets look into the hierarchy of classes for the [Message](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/message/abstracts/Message.kt#L12) in 0.35.8: [![Message Diagram.png](https://bookstack.inmo.dev/uploads/images/gallery/2021-09/scaled-1680-/message-diagram.png)](https://bookstack.inmo.dev/uploads/images/gallery/2021-09/message-diagram.png)
As you may see, it is a little bit complex and require several tools for types conversation.
## As
`as` conversations will return new type in case if it is possible. For example, when you got `Message`, you may use `asContentMessage` conversation to get message with `content`:
```kotlin
val message: Message;
println(message.asContentMessage() ?.content)
```
This code will print `null` in case when `message` is not `ContentMessage`, and `content` when is.
## Require
`require` works like `as`, but instead of returning nullable type, it will always return object with required type OR throw `ClassCastException`:
```kotlin
val message: Message;
println(message.requireContentMessage().content)
```
This code will throw exception when message is not `ContentMessage` and print `content` when is.
## When
`when` extensions will call passed `block` when type is correct. For example:
```kotlin
val message: Message;
message.whenContentMessage {
println(it.content)
}
```
Code placed above will print `content` when `message` is `ContentMessage` and do nothing when not

View File

@ -0,0 +1,57 @@
# Updates with flows
Of course, in most cases here we will look up the way of using [utils extnsions](https://github.com/InsanusMokrassar/TelegramBotAPI/tree/master/tgbotapi.utils/), but you may read deeper about updates retrieving [here](https://bookstack.inmo.dev/books/telegrambotapi/chapter/updates-retrieving).
## Phylosophy of `Flow` updates retrieving
In most updates retrieving processes there are two components: [UpdatesFiler](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/updateshandlers/UpdatesFilter.kt) and its inheritor [FlowsUpdatesFilter](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/updateshandlers/FlowsUpdatesFilter.kt#L12). It is assumed, that you will do several things in your app to handle updates:
* Create your `UpdatesFilter` (for example, with [flowsUpdatesFilter](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.utils.updates/flows-updates-filter.html) factory)
* Set it up (in case of `flowsUpdatesFilter` you will set up updates handling in the lambda passed to this factory)
* Provide updates to this filter with [filter#asUpdateReceiver](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.updateshandlers/-updates-filter/as-update-receiver.html) object
Let's look how it works with the factory above:
```kotlin
// Step 1 - create filter
val filter = flowsUpdatesFilter {
// Step 2 - set up handling. In this case we will print any message from group or user in console
messageFlow.onEach {
println(it)
}.launchIn(someCoroutineScope)
}
// Step 3 - passing updates to filter
bot.getUpdates().forEach {
filter.asUpdatesReceiver(it)
}
```
## Long polling
Some example with long polling has been described above. But it is more useful to use some factories for it. In this page we will look for simple variant with [TelegramBot#longPolling](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.extensions.utils.updates.retrieving/long-polling.html). So, with this function, your handling of updates will looks like:
```kotlin
val bot = telegramBot("TOKEN")
bot.longPolling {
messageFlow.onEach {
println(it)
}.launchIn(someCoroutineScope)
}.join()
```
This example looks like the example above with three steps, but there are several important things here:
* You **do not** manage retrieving of updates by hands
* `.join()` will suspend your function 😊 `longPolling` function returns `Job` and you may use it to:
* `cancel` working of long polling (just call `job.cancel()`)
* `join` and wait while the work of `longPolling` will not be completed (it will works infinity if you will not cancel it anywhere)
* [FlowsUpdatesFilter](https://tgbotapi.inmo.dev/docs/dev.inmo.tgbotapi.updateshandlers/-flows-updates-filter/index.html) has been created under the hood of `longPolling` function
## Results and `What is next?`
As a result you can start listen updates and react on it. Next recommended articles:
* [Behaviour Builder](https://bookstack.inmo.dev/books/telegrambotapi/page/behaviour-builder) as a variant of asynchronous handling of your bot logic
* [FSM variant of Behaviour Builder](https://bookstack.inmo.dev/books/telegrambotapi/page/behaviour-builder-with-fsm)

View File

@ -0,0 +1,100 @@
# Heroku
> NOTE: **Preview reading**
>
> It is recommended to visit our pages about [UpdatesFilters](updates-filters.html) and [Webhooks](webhooks.html) to have more clear understanding about what is happening in this examples page
[Heroku](https://heroku.com/) is a popular place for bots hosting. In common case you will need to configure webhooks for your server to include getting updates without problems. There are several things related to heroku you should know:
* Heroku apps by default accessible via `https://<app name>.herokuapp.com/`
* Heroku provide one port to be proxied for the link above. You can retrieve number of this port by calling `System.getenv("PORT").toInt()`
* Currently (`Sat Aug 15 5:04:21 +00 2020`) there is only one official server engine for ktor which is correctly working with Heroku: [Tomcat server engine](https://ktor.io/servers/configuration.html#tomcat)
> NOTE: **Server configuration alternatives**
> Here will be presented variants of configuration of webhooks and starting server. You always able to set webhook manualy, create your own ktor server and include webhooks handling in it or create and start server with only webhooks handling. More info you can get on page [Webhooks](webhooks.html)
### Short example with Behaviour Builder
```kotlin
suspend fun main {
// This subroute will be used as random webhook subroute to improve security according to the recommendations of Telegram
val subroute = uuid4().toString()
// Input/Output coroutines scope more info here: https://kotlinlang.org/docs/coroutines-guide.html
val scope = CoroutineScope(Dispatchers.IO)
// Here will be automatically created bot and available inside of lambda where you will setup your bot behaviour
telegramBotWithBehaviour(
// Pass TOKEN inside of your application environment variables
System.getenv("TOKEN"),
scope = scope
) {
// Set up webhooks and start to listen them
setWebhookInfoAndStartListenWebhooks(
// Automatic env which will be passed by heroku to the app
System.getenv("PORT").toInt(),
// Server engine. More info here: https://ktor.io/docs/engines.html
Tomcat,
// Pass URL environment variable via settings of application. It must looks like https://<app name>.herokuapp.com
SetWebhook("${System.getenv("URL").removeSuffix("/")}/$subroute"),
// Just callback which will be called when exceptions will happen inside of webhooks
{
it.printStackTrace()
},
// Set up listen requests from outside
"0.0.0.0",
// Set up subroute to listen webhooks to
subroute,
// BehaviourContext is the CoroutineScope and it is recommended to pass it inside of webhooks server
scope = this,
// BehaviourContext is the FlowsUpdatesFilter and it is recommended to pass its asUpdateReceiver as a block to retrieve all the updates
block = asUpdateReceiver
)
// Test reaction on each command with reply and text `Got it`
onUnhandledCommand {
reply(it, "Got it")
}
}
// Just potentially infinite await of bot completion
scope.coroutineContext.job.join()
}
```
### Configuration example without Behaviour Builder
```kotlin
// This subroute will be used as random webhook subroute to improve security according to the recommendations of Telegram
val subroute = uuid4().toString()
val bot = telegramBot(TOKEN)
val scope = CoroutineScope(Dispatchers.Default)
val filter = flowsUpdatesFilter {
messageFlow.onEach {
println(it) // will be printed
}.launchIn(scope)
}
val subroute = UUID.randomUUID().toString() // It will be used as subpath for security target as recommended by https://core.telegram.org/bots/api#setwebhook
val server = bot.setWebhookInfoAndStartListenWebhooks(
// Automatic env which will be passed by heroku to the app
System.getenv("PORT").toInt(),
// Server engine. More info here: https://ktor.io/docs/engines.html
Tomcat,
// Pass URL environment variable via settings of application. It must looks like https://<app name>.herokuapp.com
SetWebhook("${System.getenv("URL").removeSuffix("/")}/$subroute"),
// Just callback which will be called when exceptions will happen inside of webhooks
{
it.printStackTrace()
},
// Set up listen requests from outside
"0.0.0.0",
// Set up subroute to listen webhooks to
subroute,
scope = scope,
block = filter.asUpdateReceiver
)
server.environment.connectors.forEach {
println(it)
}
server.start(false)
```

View File

@ -0,0 +1,59 @@
# Long polling
Long polling is a technology of getting updates for cases you do not have some dedicated server or you have no opportunity to receive updates via [webhooks](webhooks.html). More about this you can read in [wiki](https://en.wikipedia.org/wiki/Push_technology#Long_polling).
## Related topics
* [Updates filters](updates-filters.html)
## Long polling in this library
There are a lot of ways to include work with long polling:
* `RequestsExecutor#longPollingFlow` Is the base way to get all updates cold Flow. Remember, that __this flow will not be launched automatically__
* `RequestsExecutor#startGettingOfUpdatesByLongPolling` Old and almost deprecated way
* `RequestsExecutor#longPolling` Works like `startGettingOfUpdatesByLongPolling` but shorted in a name :)
* `RequestsExecutor#createAccumulatedUpdatesRetrieverFlow` Works like `longPollingFlow`, but flow inside will return only the updates accumulated at the moment of calls (all new updates will not be passed throw this flow)
* `RequestsExecutor#retrieveAccumulatedUpdates` Use `createAccumulatedUpdatesRetrieverFlow` to perform all accumulated updates
* `RequestsExecutor#flushAccumulatedUpdates` Works like `retrieveAccumulatedUpdates` but perform all updates directly in a place of calling
* By yourself with `GetUpdates` request or `RequestsExecutor#getUpdates` extension
### longPolling
`longPolling` is a simple way to start getting updates and work with bot:
```kotlin
val bot = telegramBot(token)
bot.longPolling(
textMessages().subscribe(scope) { // here "scope" is a CoroutineScope
println(it) // will be printed each update from chats with messages
}
)
```
### startGettingOfUpdatesByLongPolling
The main aim of `startGettingOfUpdatesByLongPolling` extension was to provide more simple way to get updates in automatic mode:
```kotlin
val bot = telegramBot(token)
bot.startGettingOfUpdatesByLongPolling(
{
println(it) // will be printed each update from chats with messages
}
)
```
The other way is to use the most basic `startGettingOfUpdatesByLongPolling` extension:
```kotlin
val bot = telegramBot(token)
bot.startGettingOfUpdatesByLongPolling {
println(it) // will be printed each update
}
```
## See also
* [Webhooks](webhooks.html)
* [Updates filters](updates-filters.html)

View File

@ -0,0 +1,123 @@
# Updates filters
Due to the fact, that anyway you will get updates in one format (`Update` objects), some time ago was solved to create one point of updates filters for more usefull way of updates handling
## UpdatesFilter
`UpdatesFilter` currently have two properties:
* `asUpdateReceiver` - required to represent this filter as common updates receiver which able to get any `Update`
* `allowedUpdates` - required to determine, which updates are usefull for this filter
Anyway, this filter can't work with updates by itself. For retrieving updates you should pass this filter to some of getting updates functions ([long polling](long-polling) or [webhooks](webhooks.html)).
### SimpleUpdatesFilter
`SimpleUpdatesFilter` is a simple variant of filters. It have a lot of `UpdateReceiver` properties which can be set up on creating of this object. For example, if you wish to get messages from chats (but not from channels), you can use next snippet:
```kotlin
SimpleUpdatesFilter {
println(it)
}
```
### FlowsUpdatesFilter
A little bit more modern way is to use `FlowsUpdatesFilter`. It is very powerfull API of Kotlin Coroutines Flows, built-in support of additional extensions for `FlowsUpdatesFilter` and `Flow<...>` receivers and opportunity to split one filter for as much receivers as you want. Filter creating example:
```kotlin
val scope = CoroutineScope(Dispatchers.Default)
flowsUpdatesFilter {
messageFlow.onEach {
println(it)
}.launchIn(scope)
}
```
#### Combining of flows
In cases you need not separate logic for handling of messages from channels and chats there are three ways to combine different flows into one:
* Standard `plus` operation and handling of different flows:
```kotlin
flowsUpdatesFilter {
(messageFlow + channelPostFlow).onEach {
println(it) // will be printed each message update from channels and chats both
}.launchIn(scope)
}
```
* TelegramBotAPI library support function `aggregateFlows`:
```kotlin
flowsUpdatesFilter {
aggregateFlows(
scope,
messageFlow,
channelPostFlow
).onEach {
println(it) // will be printed each message update from channels and chats both
}.launchIn(scope)
}
```
* `FlowsUpdatesFilter` extensions:
```kotlin
flowsUpdatesFilter {
allSentMessagesFlow.onEach {
println(it) // will be printed each message update from channels and chats both
}.launchIn(scope)
}
```
#### Types filtering
`FlowsUpdatesFilter` have a lot of extensions for messages types filtering:
```kotlin
flowsUpdatesFilter {
textMessages(scope).onEach {
println(it) // will be printed each message from channels and chats both with content only `TextContent`
}.launchIn(scope)
}
```
The same things were created for media groups:
```kotlin
flowsUpdatesFilter {
mediaGroupMessages(scope).onEach {
println(it) // will be printed each media group messages list from both channels and chats without filtering of content
}.launchIn(scope)
mediaGroupPhotosMessages(scope).onEach {
println(it) // will be printed each media group messages list from both channels and chats with PhotoContent only
}.launchIn(scope)
mediaGroupVideosMessages(scope).onEach {
println(it) // will be printed each media group messages list from both channels and chats with VideoContent only
}.launchIn(scope)
}
```
Besides, there is an opportunity to avoid separation on media groups and common messages and receive photos and videos content in one flow:
```kotlin
flowsUpdatesFilter {
sentMessagesWithMediaGroups(scope).onEach {
println(it) // will be printed each message including each separated media group message from both channels and chats without filtering of content
}.launchIn(scope)
photoMessagesWithMediaGroups(scope).onEach {
println(it) // will be printed each message including each separated media group message from both channels and chats with PhotoContent only
}.launchIn(scope)
videoMessagesWithMediaGroups(scope).onEach {
println(it) // will be printed each message including each separated media group message from both channels and chats with VideoContent only
}.launchIn(scope)
}
```
## See also
* [Long polling](long-polling.html)
* [Webhooks](webhooks.html)

View File

@ -0,0 +1,126 @@
# Webhooks
In telegram bot API there is an opportunity to get updates via [webhooks](https://core.telegram.org/bots/webhooks). In this case you will be able to retrieve updates without making additional requests. Most of currently available methods for webhooks are working on [ktor server](https://ktor.io/servers/index.html) for JVM. Currently, next ways are available for using for webhooks:
* `Route#includeWebhookHandlingInRoute` for [ktor server](https://ktor.io/servers/index.html)
* `Route#includeWebhookHandlingInRouteWithFlows`
* `startListenWebhooks`
* `RequestsExecutor#setWebhookInfoAndStartListenWebhooks`
### `setWebhookInfoAndStartListenWebhooks`
It is the most common way to set updates webhooks and start listening of them. Example:
```kotlin
val bot = telegramBot(TOKEN)
val filter = flowsUpdatesFilter {
// ...
}
bot.setWebhookInfoAndStartListenWebhooks(
8080, // listening port. It is required for cases when your server hidden by some proxy or other system like Heroku
CIO, // default ktor server engine. It is recommended to replace it with something like `Netty`. More info about engines here: https://ktor.io/servers/configuration.html
SetWebhook(
"address.com/webhook_route",
File("/path/to/certificate").toInputFile(), // certificate file. More info here: https://core.telegram.org/bots/webhooks#a-certificate-where-do-i-get-one-and-how
40, // max allowed updates, by default is null
filter.allowedUpdates
),
{
it.printStackTrace() // optional handling of exceptions
},
"0.0.0.0", // listening host which will be used to bind by server
"subroute", // Optional subroute, if null - will listen root of address
WebhookPrivateKeyConfig( // optional config of private key. It will be installed in server to use TLS with custom certificate. More info here: https://core.telegram.org/bots/webhooks#a-certificate-where-do-i-get-one-and-how
"/path/to/keystore.jks",
"KeystorePassword",
"Keystore key alias name",
"KeystoreAliasPassword"
),
scope, // Kotlin coroutine scope for internal transforming of media groups
filter.asUpdateReceiver
)
```
If you will use previous example, ktor server will bind and listen url `0.0.0.0:8080/subroute` and telegram will send requests to address `address.com/webhook_route` with custom certificate. Alternative variant will use the other `SetWebhook` request variant:
```kotlin
SetWebhook(
"address.com/webhook_route",
"some_file_bot_id".toInputFile(),
40, // max allowed updates, by default is null
filter.allowedUpdates
)
```
As a result, request `SetWebhook` will be executed and after this server will start its working and handling of updates.
### `startListenWebhooks`
This function is working almost exactly like previous example, but this one will not set up webhook info in telegram:
```kotlin
val filter = flowsUpdatesFilter {
// ...
}
startListenWebhooks(
8080, // listening port. It is required for cases when your server hidden by some proxy or other system like Heroku
CIO, // default ktor server engine. It is recommended to replace it with something like `Netty`. More info about engines here: https://ktor.io/servers/configuration.html
{
it.printStackTrace() // optional handling of exceptions
},
"0.0.0.0", // listening host which will be used to bind by server
"subroute", // Optional subroute, if null - will listen root of address
WebhookPrivateKeyConfig( // optional config of private key. It will be installed in server to use TLS with custom certificate. More info here: https://core.telegram.org/bots/webhooks#a-certificate-where-do-i-get-one-and-how
"/path/to/keystore.jks",
"KeystorePassword",
"Keystore key alias name",
"KeystoreAliasPassword"
),
scope, // Kotlin coroutine scope for internal transforming of media groups
filter.asUpdateReceiver
)
```
The result will be the same as in previous example: server will start its working and handling of updates on `0.0.0.0:8080/subroute`. The difference here is that in case if this bot must not answer or send some requiests - it will not be necessary to create bot for receiving of updates.
### Extensions `includeWebhookHandlingInRoute` and `includeWebhookHandlingInRouteWithFlows`
For these extensions you will need to start your server manualy. In common case it will look like:
```kotlin
val scope = CoroutineScope(Dispatchers.Default)
val filter = flowsUpdatesFilter {
// ...
}
val environment = applicationEngineEnvironment {
module {
routing {
includeWebhookHandlingInRoute(
scope,
{
it.printStackTrace()
},
filter.asUpdateReceiver
)
}
}
connector {
host = "0.0.0.0"
port = 8080
}
}
embeddedServer(CIO, environment).start(true) // will start server and wait its stoping
```
In the example above server will started and binded for listening on `0.0.0.0:8080`.
## See also
* [Updates filters](updates-filters.html)
* [Long polling](long-polling.html)

93
mkdocs.yml Normal file
View File

@ -0,0 +1,93 @@
site_name: InMo Docs
site_author: Ovsiannikov Aleksei
site_description: >-
Single documentation place for all Insanus Mokrassar libraries
site_url: https://docs.inmo.dev/
repo_name: InsanusMokrassar/docs
repo_url: https://github.com/InsanusMokrassar/docs
nav:
- Home: 'index.md'
- 'Telegram Bot API':
- Introduction:
- 'tgbotapi/introduction/before-any-bot-project.md'
- 'tgbotapi/introduction/including-in-your-project.md'
- 'tgbotapi/introduction/proxy-setup.md'
- 'tgbotapi/introduction/first-bot.md'
- Updates retrieving:
- 'tgbotapi/updates/updates-filters.md'
- 'tgbotapi/updates/long-polling.md'
- 'tgbotapi/updates/webhooks.md'
- 'tgbotapi/updates/heroku.md'
- Logic handling: 'tgbotapi/logic/'
- DSLs: 'tgbotapi/dsls/'
use_directory_urls: false
theme:
name: material
hljs_languages:
- yaml
- kotlin
- javascript
- xml
- groovy
features:
- announce.dismiss
- content.action.edit
- content.action.view
- content.code.annotate
- content.code.copy
# - content.tabs.link
- content.tooltips
# - header.autohide
# - navigation.expand
- navigation.footer
- navigation.indexes
# - navigation.instant
# - navigation.prune
- navigation.sections
- navigation.tabs
# - navigation.tabs.sticky
- navigation.top
- navigation.tracking
- search.highlight
- search.share
- search.suggest
- toc.follow
# - toc.integrate
font:
text: Roboto
code: Roboto Mono
markdown_extensions:
- toc:
permalink: True
- smarty
- callouts
plugins:
- search
- autorefs
- include_dir_to_nav
# Customization
#extra:
# annotate:
# json: [.s2]
# analytics:
# provider: google
# property: !ENV GOOGLE_ANALYTICS_KEY
# social:
# - icon: fontawesome/brands/github
# link: https://github.com/squidfunk
# - icon: fontawesome/brands/gitter
# link: https://gitter.im/squidfunk/mkdocs-material
# - icon: fontawesome/brands/docker
# link: https://hub.docker.com/r/squidfunk/mkdocs-material/
# - icon: fontawesome/brands/python
# link: https://pypi.org/project/mkdocs-material/
# - icon: fontawesome/brands/mastodon
# link: https://fosstodon.org/@squidfunk
# - icon: fontawesome/brands/twitter
# link: https://twitter.com/squidfunk