Easy to use, asynchronous wrapper for the Telegram Bot API (support v4.3) written in pure Kotlin. Inspired by aiogram.
Table of content:
- Getting started
- Writing your first bot
- General API Documentation
- Advanced use of the API
- The Telegram Chat Group
- Examples
- Bots using this API
- Wrapping Notes
- TODO
- Special thanks to
Install to your project:
- Using Gradle.kts:
implementation("rocks.waffle.telekt:telekt:0.6.7")
Also this project uses kotlinx.serialization, so you need to add kotlinx repository:
repositories {
maven("https://kotlin.bintray.com/kotlinx")
}
Note: While API is ready-to-use, it is still in beta. Any new version can break backward compatibility, see change logs and be careful.
It is presumed that you have obtained an API token with @BotFather. We will call this token TOKEN
.
Furthermore, you have basic knowledge of the Kotlin programming language and more importantly the Telegram Bot API.
TeleKt splits calling tg api methods and dispatching incoming updates.
- For first feature there is class Bot that encapsulates all API calls in a single class.
- For second — class Dispatcher provide several ways to listen for incoming updates (e.g. messages).
Create a file called echobot.kt
.
Then, open the file and create an instance of the Bot
and Dispatcher
classes.
import rocks.waffle.telekt.bot.*
import rocks.waffle.telekt.dispatcher.*
fun main() {
val bot = Bot("TOKEN")
val dp = Dispatcher(bot)
}
Note: Make sure to actually replace TOKEN with your own API token.
After that declaration, we need to register some so-called message handlers. Message handlers define filters which a message must pass. If a message passes the filter, the passed function is called and the incoming message is passed as an argument.
Let's define a message handler which handles incoming /start
and /help
commands.
dp.messageHandler(CommandFilter("start", "help")) { message: Message ->
bot.answerOn(message, "Howdy, how are you doing?")
}
Let's add another handler:
dp.messageHandler { message ->
bot.answerOn(message, message.text ?: "this message has no text")
}
This one echoes all incoming text messages back to the sender. We doesn't pass any filters, so all messages will income here.
Note: all handlers are tested in the order in which they were declared
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
dp.poll()
Alright, that's it! Our source file now looks like this:
import rocks.waffle.telekt.bot.*
import rocks.waffle.telekt.dispatcher.*
import rocks.waffle.telekt.types.Message
import rocks.waffle.telekt.contrib.filters.CommandFilter
suspend fun main() {
// ^^^^ NOTE: this lib is async, so you need to run it from suspending funciton
val bot = Bot("TOKEM")
val dp = Dispatcher(bot)
dp.messageHandler(CommandFilter("start", "help")) { message: Message ->
bot.answerOn(message, "Hi there 0/")
}
dp.messageHandler { message ->
bot.answerOn(message, message.text ?: "this message has no text")
}
dp.poll()
}
After running bot, test it by sending commands ('/start' and '/help') and arbitrary text messages.
Full example you can see at there
All types are defined in telekt.types package.
They are all completely in line with the Telegram API's definition of the types, except that all field renamed from snake_case
to camelCase
(like message.message_id
=> message.messageId
). Thus, attributes such as messageId
can be accessed directly with message.messageId
.
The Message class also has a contentType
attribute, which defines the type of the Message.
All API methods are located in the Bot class.
Outlined below are some general use cases of the API.
A message handler is a function that is given to messageHandler
function of a Dispatcher instance.
Message handlers consist of 0, one or multiple filters.
Each filter's test(...)
function must return True
for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way:
dp.messageHandler(*filters) { /* ... */ }
fun functionName(message: Message) { /* ... */ }
dp.messageHandler(*filters, block = ::functionName)
functionName
is not bound to any restrictions. Any function name is permitted with message handlers. The function must accept at most one argument, which will be the message event that the function must handle.
filters
is a vararg array of Filters.
One handler may have multiple filters.
There is also DSL
-like builder for registration handlers:
dp.dispatch {
messages {
handle(/* filters here */) { /* message handler here */ }
}
callbackQuerys {
handle(/* filters here */) { /* callback query handler here */ }
}
// and so on
}
Important: all handlers are tested in the order in which they were declared
All other handlers (callbackQuery, editedMessage, etc) work the same way.
Bot.me is lazy started coroutine builded with async{}
builder that just calls bot.getMe()
. Only bot creator can change bot user (via BotFather) so in most cases it's safe to use it.
bot.me.await()
Reply markup
All send<Something>
functions of Bot take an optional replyMarkup
argument.
This argument must be an instance of ReplyKeyboardMarkup
, InlineKeyboardMarkup
, ReplyKeyboardRemove
or ForceReply
, which are defined in markup.kt.
More information about Inline mode.
Refer Bot Api for extra details
// TODO: write about
- dispatch modes (need to be implemented)
- FSM
- HandlerScope, HandlerContext
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
import rocks.waffle.telekt.util.splitByLenght
val largeText = "Really large text here"
// Split the text each 3000 characters.
// splitByLenght returns a list with the splitted text.
val texts = largeText.splitByLenght() // you can also pass different max lenght
texts.forEach { bot.sendMessage(chat_id, it) }
// TODO
This lib is using KotlinLogging. In examples we are using logback.
No questions there yet, ask me something! :)
Get help. Discuss. Chat.
- Join the TeleKt Telegram Chat Group
- Or join Russian TeleKt Chat Group
- Also join our News Channel
All examples are located in examples directory
No one yet, you can become first!
Send a telegram message to @wafflelapkin,
or send an email to waffle.lapkin@gmail.com
or write in our group,
or open an issue on github.
Note some things about tg bot api wrapping:
- All methods that return
True
in tg bot api (like unbanChatMember), in TeleKt returnUnit
- All
edit*
methods splited in 2 overloads:- For messages sent by the bot (return
Message
) - For inline messages (return
Unit
)
- For messages sent by the bot (return
- All names have been changed to match with coding conventions
- Some methods that accept const strings in tg bot api (like sendChatAction), in TeleKt accept enums
- All
chatId
params is of typeRecipient
cause Kotlin haven't algebraic types.
Things between 'now' and 'release 1.0'
- Webhooks
- Receiving updates
- Answer into webhook
- Add option to get current webhook state (Running, Stopped, Closed) (?)
- Middlewares (like in aiogram)
- Implement middlewares
- Write some built-in middlewares
- timeit
- logging
- i18n (?)
- Add more examples
- Docs
- Write more comments in code
- Write comments in telegram types
- Write comments everywhere else
- Add more storages
- Mongo db
- PostrgeSQL
- Safe update dispatching (with different
Dispatcher
?) - Anti-spam and/or api request limits handling
- gt22 and mrAppleXZ from pearx team — for answering my stupid questions
- Russian Kotlin Community in telegram — also for answering my stupid questions
- Alex Root Junior — for writing aiogram
- Dmitriy Shilnikov — for fixing jackson deserialization (Actually now TeleKt use kotlinx.serialization instead of jackson, but anyway thanks)
- afdw — for cleaning
logo.svg
- My parents — for love and support ❤️