Releases: tbot-rs/tbot
v0.5.2
We've worked hard during the last two weeks, and prepared two big features, a few smaller ones, аnd, of course, we didn't forget about Bot API 4.7. What's even better is that all the new features don't break compatibility, so we're releasing them in version 0.5.2!
Bot API 4.7 (#33)
Let's start with Bot API 4.7 first. Recently, Telegram released a feature everyone's been asking for years — rolling dice in chats. We can't help but support it in tbot
, so we've implemented the SendDice
method and the dice
handler on EventLoop
. Telegram now lets bots see and modify the list of their commands, and you can do it with the GetMyCommands
and SetMyCommands
methods. Lastly, bots now can create animated sticker sets, and the CreateNewStickerSet
and AddStickerToSet
methods have been updated accordingly.
tbot::state
, the stateful event loop (!173)
The next feature is amazing and you might've really wanted it. The new module tbot::state
introduces StatefulEventLoop
, as well as a couple state stores you'll likely need. To create a stateful event loop, call Bot::stateful_event_loop
instead of Bot::event_loop
:
let mut bot = tbot::from_env!("BOT_TOKEN").stateful_event_loop(initial_state);
The initial state can be absolutely anything (that you can share between threads), and all handlers will receive the state wrapped in an Arc
as their second argument:
use tokio::sync::Mutex;
let mut bot = tbot::from_env!("BOT_TOKEN")
.stateful_event_loop(Mutex::new(0));
bot.text(|context, counter| async move {
counter.lock().await += 1;
});
Each method that sets a handler has a corresponding method with the _if
postfix, that take a predicate and run the handler if the predicate returns true
:
let is_on_second_stage = |context, state| async move {
state.chats.get(&*context).await.stage == 2
};
bot.text_if(is_on_second_stage, |context, state| async move { .. });
The rest of StatefulEventLoop
's API is pretty much the same as the stateless's one, excepts for methods that go from a stateless to a stateful event loop and back or switch the state.
The last thing about state is two state stores tbot
provides out of the box: Chats
and Messages
, which store some state per each chat and message respectively. If you want to learn more, go check out tbot::state
's docs. Don't forget to take a look at our new two examples: a questionary (warning: zealotry!) and an anonymous messenger.
tbot::markup
, easy and fearless work with markup (!180)
If you ever worked with markup manually, you know it's pretty hard to escape all user input correctly so that it won't break your markup. tbot::markup
comes to the rescue, simplifying working with markup and making it fearless. It contains basic formatters like bold
and italic
, which you then pass to a markup formatter — markdown_v2
or html
— which does all the work applying formatters and escaping strings when turned into a string:
use tbot::markup::{bold, italic, markdown_v2};
let message = markdown_v2(bold((
"*This is <b>old, ",
italic("and this is bold and italic!"),
))).to_string();
println!("{}", message); // *\*This is <b\>old, _and this is bold and italic\!_*
To learn more, visit tbot::markup
's docs.
Miscellaneous
- Due to how the
ChatMethods
trait and its methods were defined, you couldn't write a generic handler with the context's trait bound includingChatMethods
. because when you'd call a method, the compiler would error saying that the method's input arguments need to live longer than the context. We fixed the lifetimes on the methods (this doesn't break anything, as it shortens inputs' lifetime, you can't use lifetimes on methods and the traits are sealed), but for compatibility we left the lifetime onChatsMethods
and other context traits. (!181) - Due to how Cargo handles features, when we introduced the
rustls
feature,hyper-proxy
was downloaded despite theproxy
feature being disabled. Instead of using ugly hacks, we decided to make proxy available without enabling theproxy
feature. (!183) - If you ever need to trace through
tbot
when debugging something,tbot
now logs all method calls and how the event loop handles updates usingtracing
. To see it, enabletbot
's logs on the trace level, seetracing
's Readme to learn how to do it. (!176) - Previously,
tbot
's webhook server accepted updates only on/
and ignored updates coming to all other URLs. Now, the URL on whichtbot
accepts updates can be configured withWebhook::accept_updates_on
. (!179) - We clarified the docs regarding commands with a bot's username (
/command@username
). Astbot
needs to know the bot's username to properly handle such commands in all circumstances, the username needs to be configured explicitly using eitherEventLoop::username
orEventLoop::fetch_username
. Now the docs ofcommand
and similar handler setters hint to these methods. (#34) - If Telegram returns
retry_after
whentbot
polls for updates,tbot
will now respect it. (!177)
It's a lot of features for a single update. Stay home and write bots in Rust with tbot
!
v0.5.1
The version 0.5.1 of tbot fixes the wrong assumption that all code blocks have a defined language, which causes parsing errors whenever a code block without a language is received. This fix is a potential breaking change as it changes the type of code blocks' language from String to Option. However, we tested the patch on open-source bots from GitHub and they continue to compile. Even if this patch affects your bot, it points to the erroneous assumption on your side which should be fixed.
This issue was reported in issue #32 and fixed in merge request !171. Refer to them for additional information about the bug.
v0.5
We're happy to announce the release of tbot
v0.5!
In this version, tbot
has gained support for Bot API v4.6 (#26). This means that
you can now make use of Polls 2.0, as well as get more information about your
bot and the language of code blocks. Unfortunately, this led to a few breaking
changes:
- The
GetMe
method now returns a new struct,types::user::Me
, which is
composed of the fields only returned byGetMe
and theuser
field,
containing the information which is present in all instances of theUser
type; - We fixed a bug regarding reply buttons: though type fields of
ReplyButton
are mutually exclusive,tbot
didn't prevent one from setting both types at
the same time. This was fixed in !158, but it did introduce a breaking change.
Now, all request types are contained in one enum, which you pass to the
request
setter of areply::Button
instance. - To support language tags, we had to change the
message::entity::Kind::Pre
variant from a unit one to a tuple. But this one is easy to figure out 😉
Thanks to @uwinx, you can now easily add a handler for several commands using
EventLoop::commands
(and edited_commands
in case you're interested in this
one) (!161):
let mut bot = tbot::from_env!(..).event_loop();
bot.commands(["start", "help"], |context| { .. });
This version also introduces the Command
context, which wraps the underlying
text update, but it also provides you with the command which triggered the
handler. But note that all command handlers now receive the new context, so you
may have to change a few typed out command context types if you ever did so. But
Command
dereferences to the underlying context and implements the appropriate
traits, so migrating to the new context won't be hard.
And the last change in this version is this small improvement (!167): if your
bot receives an edited message with an attachment one can't usually edit, you'll
see the update in tbot's message.
We hope you'll like this update! And by the way, we now have
a website, check it out :)
v0.4
Ho-ho-ho! 🎅 tbot
v0.4 is coming to town! 🛷
Didn't expect a major release on New Year? Well, we too. Who could've thought that Telegram would release a new Bot API version today? Anyway, we're already ready for it!
Starting this version, you can use rustls
with tbot
! (!145) For those who don't know what it is, it's a complete TLS implementation in Rust — a bit less of C in your Rust bots :) It's not enabled by default though, so you have to enable it manually in your Cargo.toml:
tbot = { version = "0.4", default-features = false, features = ["rustls"] }
Let's get back to Bot API v4.5. One of the most important changes is MarkdownV2, which lets you strikethrough and underline text, and also nest formatting! (!152) We're going to implement a module to make it easier to work with markup, but for now you can use parameters::Text::MarkdownV2
to forget about the dark past of messing with the markdown parser.
You can now see admins' custom titles and set titles for those admins that your bot promoted (!151). Also, bots now can read the slow mode delay in supergroups (!150) so that you don't spam too much. And how could you live without unique file IDs you can't do anything with? (!149)
Aaand back to tbot
itself. We fixed a horrible typo in chat::member::Status::Administrator
and in some doc (!147), hopefully the compiler will point that to you if you used this enum and didn't spot the typo yourself. A more important change is that we've adopted is-macro
— a crate that generates is_*
, expect_*
and taking methods (those that try to match on a variant and return Some(T)
if successful or None
otherwise) for enums (!146, !156). Because of that, some is_*
methods changed their names a bit — check the docs if you run into some problems with this. We also boxed a few types in message::Kind
, so grab a few stars from the amazing night sky and put them in your code if you need to work with that type (!155).
Have you checkout out the updated readme on our homepage? In that readme, you can find a link to a list of projects that already build upon tbot. Very proud to see that our project is already used in bots with several thousand users 😊
So, it's already 2020 at my place. We wish you a Merry Christmas, we wish you a Merry Christmas, we wish you a Merry Christmas and a Happy New Year! Write all your bots in Rust
v0.3
We're happy to announce tbot
v0.3! Here's an overview of what we've prepared
for this release.
In v0.3, tbot
has moved to std::future
and now supports async
/.await
!
For example, here's an echo bot implemented using async
/.await
:
use tbot::prelude::*;
async fn main() {
let mut bot = tbot::from_env!("BOT_TOKEN").event_loop();
bot.text(|context| {
async move {
let echo = &context.text.value;
let call_result = context.send_message(echo).call().await;
if let Err(err) = call_result {
dbg!(err);
}
}
});
bot.polling().start().await.unwrap();
}
Also, you must appreciate the getMe
example:
async fn main() -> Result<(), tbot::errors::MethodCall> {
let bot = tbot::from_env!("BOT_TOKEN");
let me = bot.get_me().call().await?;
dbg!(me);
Ok(())
}
tbot
v0.3 introduces contexts::fields
, a module that contains traits for
specific contexts fields. It may sound complicated, but what it actually means
is that you can now write handlers generic over contexts. Here's an example
of a handler that you can use for both just-sent photos and edited ones:
use std::sync::Arc;
use tbot::{contexts::fields::Photo, connectors::Connector};
async fn handle_photo<Ctx, Cnn: Connector>(context: Arc<Ctx>)
where
Ctx: Photo<Cnn>,
Cnn: Connector,
{
let photo = context.photo();
// ..
}
// ..
bot.photo(handle_photo);
bot.edited_photo(handle_photo);
Though tbot
has had webhook support since the first version, it could only
listen over HTTP, while Telegram sent updates over HTTPS only. Because of that,
you would have to use nginx to use webhooks with tbot. Now tbot
can natively
listen to updates over HTTPS! If you had this:
bot.webhook(URL, PORT).start();
you can turn it into this:
use tbot::event_loop::webhook::https::Identity;
let identity = Identity::from_pkcs12(
include_bytes!("path/to/identity.p12"),
env!("IDENTITY_PASSWORD"),
).unwrap();
bot.webhook(URL, PORT)
.certificate(include_str!("path/to/public/key/cert.pem"))
.https(identity)
.start()
.await
.unwrap();
Still, nginx is useful if you want to host several bots on one domain/IP
address or to distribute load between servers, and tbot continues to support
webhook over HTTP.
Another important change in this release is that handlers cannot be mutating
anymore, meaning that you no longer can write this:
let mut id = 0;
bot.inline(move |_| {
id += 1;
// ..
});
Instead, you should use a Mutex
(or similar) if you need to have some global
state, which scales better with time:
use tokio::sync::Mutex;
use std::sync::Arc;
let id = Arc::new(Mutex::new(0));
bot.inline(move |_| {
let id = Arc::clone(&id);
async move {
id.lock().await += 1;
}
});
tbot::bot!
was renamed totbot::from_env!
to better reflect what it does
(!107);- Most types are annotated with
#[non_exhaustive]
(leading to MSRV 1.40)
(!133); - Input types became
#[must_use]
, as well as methods returning any type
(for they're all pure) (!134, !137, !138); types::parameters::Updates
was renamed totypes::parameters::UpdateKind
(!141);- A missing method
ChatMethods::pin_chat_message
was added (!111); - A trait
Connector
was introduced to simplify writing code generic over
connectors (!118); - Method docs were updated to describe what each method does, so you no longer
need to refer to Bots API docs (!115); is_*
methods were added for enums where they were accidentially missing
(!140);- The
Token
type is no longer public: it was only used once in public code
where this additional type safety is not actually needed. It is still used
internally to allow debugging methods without revealing your token (!124).
We hope you enjoy this huge update!
v0.2.2
v0.2
v0.1.2
The v0.1.2
release of tbot
minimizes the breaking change caused by Bot API v4.4. The cause of the breaking change is Message.sticker
reused for animated stickers, which use tgs
instead of webp
, yet old code will still see the animated stickers as plain ones. tbot
addresses this by adding a separate handler for animated stickers while the old handlers for stickers won't be fired.
Also, this release adds the is_animated
field to types::sticker::Set
. In the v0.2
release, this field will be instead replaced with an enum of sticker set kinds.