use crate::{
contexts, errors,
state::StatefulEventLoop,
types::{
self, callback,
message::{
self,
text::{Entity, EntityKind, Text},
Message,
},
update,
},
Bot,
};
use std::{collections::HashMap, future::Future, sync::Arc};
use tracing::{error, instrument, trace, warn};
#[macro_use]
mod handlers_macros;
mod polling;
pub mod webhook;
pub use {polling::Polling, webhook::Webhook};
type Handlers<T> = Vec<Box<T>>;
type Map<T> = HashMap<String, Handlers<T>>;
type Handler<T> = dyn Fn(Arc<T>) + Send + Sync;
type AnimationHandler = Handler<contexts::Animation>;
type AudioHandler = Handler<contexts::Audio>;
type ChosenInlineHandler = Handler<contexts::ChosenInline>;
type CommandHandler = Handler<contexts::Command<contexts::Text>>;
type ConnectedWebsiteHandler = Handler<contexts::ConnectedWebsite>;
type ContactHandler = Handler<contexts::Contact>;
type CreatedGroupHandler = Handler<contexts::CreatedGroup>;
type DataCallbackHandler = Handler<contexts::DataCallback>;
type DeletedChatPhotoHandler = Handler<contexts::DeletedChatPhoto>;
type DiceHandler = Handler<contexts::Dice>;
type DocumentHandler = Handler<contexts::Document>;
type EditedAnimationHandler = Handler<contexts::EditedAnimation>;
type EditedAudioHandler = Handler<contexts::EditedAudio>;
type EditedCommandHandler = Handler<contexts::Command<contexts::EditedText>>;
type EditedDocumentHandler = Handler<contexts::EditedDocument>;
type EditedLocationHandler = Handler<contexts::EditedLocation>;
type EditedPhotoHandler = Handler<contexts::EditedPhoto>;
type EditedTextHandler = Handler<contexts::EditedText>;
type EditedVideoHandler = Handler<contexts::EditedVideo>;
type GameCallbackHandler = Handler<contexts::GameCallback>;
type GameHandler = Handler<contexts::Game>;
type InlineHandler = Handler<contexts::Inline>;
type InvoiceHandler = Handler<contexts::Invoice>;
type LeftMemberHandler = Handler<contexts::LeftMember>;
type LocationHandler = Handler<contexts::Location>;
type MigrationHandler = Handler<contexts::Migration>;
type NewChatPhotoHandler = Handler<contexts::NewChatPhoto>;
type NewChatTitleHandler = Handler<contexts::NewChatTitle>;
type NewMembersHandler = Handler<contexts::NewMembers>;
type PassportHandler = Handler<contexts::Passport>;
type PaymentHandler = Handler<contexts::Payment>;
type PhotoHandler = Handler<contexts::Photo>;
type PinnedMessageHandler = Handler<contexts::PinnedMessage>;
type PollHandler = Handler<contexts::Poll>;
type PollAnswerHandler = Handler<contexts::PollAnswer>;
type PreCheckoutHandler = Handler<contexts::PreCheckout>;
type ShippingHandler = Handler<contexts::Shipping>;
type StickerHandler = Handler<contexts::Sticker>;
type TextHandler = Handler<contexts::Text>;
type UnhandledHandler = Handler<contexts::Unhandled>;
type UpdatedPollHandler = Handler<contexts::UpdatedPoll>;
type UpdateHandler = Handler<contexts::Update>;
type VenueHandler = Handler<contexts::Venue>;
type VideoHandler = Handler<contexts::Video>;
type VideoNoteHandler = Handler<contexts::VideoNote>;
type VoiceHandler = Handler<contexts::Voice>;
#[must_use]
pub struct EventLoop {
bot: Bot,
username: Option<String>,
command_handlers: Map<CommandHandler>,
edited_command_handlers: Map<EditedCommandHandler>,
after_update_handlers: Handlers<UpdateHandler>,
animation_handlers: Handlers<AnimationHandler>,
audio_handlers: Handlers<AudioHandler>,
before_update_handlers: Handlers<UpdateHandler>,
chosen_inline_handlers: Handlers<ChosenInlineHandler>,
contact_handlers: Handlers<ContactHandler>,
connected_website_handlers: Handlers<ConnectedWebsiteHandler>,
created_group_handlers: Handlers<CreatedGroupHandler>,
data_callback_handlers: Handlers<DataCallbackHandler>,
deleted_chat_photo_handlers: Handlers<DeletedChatPhotoHandler>,
dice_handlers: Handlers<DiceHandler>,
document_handlers: Handlers<DocumentHandler>,
edited_animation_handlers: Handlers<EditedAnimationHandler>,
edited_audio_handlers: Handlers<EditedAudioHandler>,
edited_document_handlers: Handlers<EditedDocumentHandler>,
edited_location_handlers: Handlers<EditedLocationHandler>,
edited_photo_handlers: Handlers<EditedPhotoHandler>,
edited_text_handlers: Handlers<EditedTextHandler>,
edited_video_handlers: Handlers<EditedVideoHandler>,
game_callback_handlers: Handlers<GameCallbackHandler>,
game_handlers: Handlers<GameHandler>,
inline_handlers: Handlers<InlineHandler>,
invoice_handlers: Handlers<InvoiceHandler>,
left_member_handlers: Handlers<LeftMemberHandler>,
location_handlers: Handlers<LocationHandler>,
migration_handlers: Handlers<MigrationHandler>,
new_chat_photo_handlers: Handlers<NewChatPhotoHandler>,
new_chat_title_handlers: Handlers<NewChatTitleHandler>,
new_members_handlers: Handlers<NewMembersHandler>,
passport_handlers: Handlers<PassportHandler>,
payment_handlers: Handlers<PaymentHandler>,
photo_handlers: Handlers<PhotoHandler>,
pinned_message_handlers: Handlers<PinnedMessageHandler>,
poll_handlers: Handlers<PollHandler>,
poll_answer_handlers: Handlers<PollAnswerHandler>,
pre_checkout_handlers: Handlers<PreCheckoutHandler>,
shipping_handlers: Handlers<ShippingHandler>,
sticker_handlers: Handlers<StickerHandler>,
text_handlers: Handlers<TextHandler>,
unhandled_handlers: Handlers<UnhandledHandler>,
updated_poll_handlers: Handlers<UpdatedPollHandler>,
venue_handlers: Handlers<VenueHandler>,
video_handlers: Handlers<VideoHandler>,
video_note_handlers: Handlers<VideoNoteHandler>,
voice_handlers: Handlers<VoiceHandler>,
}
impl EventLoop {
pub(crate) fn new(bot: Bot) -> Self {
Self {
bot,
username: None,
command_handlers: HashMap::new(),
edited_command_handlers: HashMap::new(),
after_update_handlers: Vec::new(),
animation_handlers: Vec::new(),
audio_handlers: Vec::new(),
before_update_handlers: Vec::new(),
chosen_inline_handlers: Vec::new(),
contact_handlers: Vec::new(),
connected_website_handlers: Vec::new(),
created_group_handlers: Vec::new(),
data_callback_handlers: Vec::new(),
deleted_chat_photo_handlers: Vec::new(),
dice_handlers: Vec::new(),
document_handlers: Vec::new(),
edited_animation_handlers: Vec::new(),
edited_audio_handlers: Vec::new(),
edited_document_handlers: Vec::new(),
edited_location_handlers: Vec::new(),
edited_photo_handlers: Vec::new(),
edited_text_handlers: Vec::new(),
edited_video_handlers: Vec::new(),
game_callback_handlers: Vec::new(),
game_handlers: Vec::new(),
inline_handlers: Vec::new(),
invoice_handlers: Vec::new(),
left_member_handlers: Vec::new(),
location_handlers: Vec::new(),
migration_handlers: Vec::new(),
new_chat_photo_handlers: Vec::new(),
new_chat_title_handlers: Vec::new(),
new_members_handlers: Vec::new(),
passport_handlers: Vec::new(),
payment_handlers: Vec::new(),
photo_handlers: Vec::new(),
pinned_message_handlers: Vec::new(),
poll_handlers: Vec::new(),
poll_answer_handlers: Vec::new(),
pre_checkout_handlers: Vec::new(),
shipping_handlers: Vec::new(),
sticker_handlers: Vec::new(),
text_handlers: Vec::new(),
unhandled_handlers: Vec::new(),
updated_poll_handlers: Vec::new(),
venue_handlers: Vec::new(),
video_handlers: Vec::new(),
video_note_handlers: Vec::new(),
voice_handlers: Vec::new(),
}
}
pub fn into_stateful<S>(self, state: S) -> StatefulEventLoop<S>
where
S: Send + Sync + 'static,
{
StatefulEventLoop::new(self, state)
}
pub fn username(&mut self, username: String) {
self.username = Some(username);
}
pub fn polling(self) -> Polling {
Polling::new(self)
}
pub fn webhook(self, url: &str, port: u16) -> Webhook<'_> {
Webhook::new(self, url, port)
}
pub fn command<H, F>(&mut self, command: &'static str, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> F)
+ Send
+ Sync
+ 'static,
F: Future<Output = ()> + Send + 'static,
{
self.command_handlers
.entry(command.to_string())
.or_insert_with(Vec::new)
.push(Box::new(move |context| {
tokio::spawn(handler(context));
}));
}
pub fn command_if<H, HF, P, PF>(
&mut self,
command: &'static str,
predicate: P,
handler: H,
) where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::Text>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.command(command, move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
pub fn commands<Cm, H, F>(&mut self, commands: Cm, handler: H)
where
Cm: IntoIterator<Item = &'static str>,
F: Future<Output = ()> + Send + 'static,
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> F)
+ Send
+ Sync
+ 'static,
{
let handler = Arc::new(handler);
for command in commands {
let handler = Arc::clone(&handler);
self.command_handlers
.entry(command.to_string())
.or_insert_with(Vec::new)
.push(Box::new(move |context| {
tokio::spawn(handler(context));
}));
}
}
pub fn commands_if<Cm, H, HF, P, PF>(
&mut self,
commands: Cm,
predicate: P,
handler: H,
) where
Cm: IntoIterator<Item = &'static str>,
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::Text>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.commands(commands, move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
fn will_handle_command(&self, command: &str) -> bool {
self.command_handlers.contains_key(command)
}
fn run_command_handlers(
&self,
command: &str,
context: &Arc<contexts::Command<contexts::Text>>,
) {
if let Some(handlers) = self.command_handlers.get(command) {
for handler in handlers {
handler(context.clone());
}
}
}
pub fn start<H, F>(&mut self, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> F)
+ Send
+ Sync
+ 'static,
F: Future<Output = ()> + Send + 'static,
{
self.command("start", handler);
}
pub fn start_if<H, HF, P, PF>(&mut self, predicate: P, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::Text>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.start(move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
pub fn settings<H, F>(&mut self, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> F)
+ Send
+ Sync
+ 'static,
F: Future<Output = ()> + Send + 'static,
{
self.command("settings", handler);
}
pub fn settings_if<H, HF, P, PF>(&mut self, predicate: P, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::Text>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.settings(move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
pub fn help<H, F>(&mut self, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> F)
+ Send
+ Sync
+ 'static,
F: Future<Output = ()> + Send + 'static,
{
self.command("help", handler);
}
pub fn help_if<H, HF, P, PF>(&mut self, predicate: P, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::Text>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::Text>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.help(move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
pub fn edited_command<H, F>(&mut self, command: &'static str, handler: H)
where
H: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> F)
+ Send
+ Sync
+ 'static,
F: Future<Output = ()> + Send + 'static,
{
self.edited_command_handlers
.entry(command.to_string())
.or_insert_with(Vec::new)
.push(Box::new(move |context| {
tokio::spawn(handler(context));
}));
}
pub fn edited_command_if<H, HF, P, PF>(
&mut self,
command: &'static str,
predicate: P,
handler: H,
) where
H: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.edited_command(command, move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
pub fn edited_commands<Cm, H, F>(&mut self, commands: Cm, handler: H)
where
Cm: IntoIterator<Item = &'static str>,
F: Future<Output = ()> + Send + 'static,
H: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> F)
+ Send
+ Sync
+ 'static,
{
let handler = Arc::new(handler);
for command in commands {
let handler = Arc::clone(&handler);
self.edited_command_handlers
.entry(command.to_string())
.or_insert_with(Vec::new)
.push(Box::new(move |context| {
tokio::spawn(handler(context));
}));
}
}
pub fn edited_commands_if<Cm, H, HF, P, PF>(
&mut self,
commands: Cm,
predicate: P,
handler: H,
) where
Cm: IntoIterator<Item = &'static str>,
H: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> HF)
+ Send
+ Sync
+ 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Command<contexts::EditedText>>) -> PF)
+ Send
+ Sync
+ 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.edited_commands(commands, move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
fn will_handle_edited_command(&self, command: &str) -> bool {
self.edited_command_handlers.contains_key(command)
}
fn run_edited_command_handlers(
&self,
command: &str,
context: &Arc<contexts::Command<contexts::EditedText>>,
) {
if let Some(handlers) = self.edited_command_handlers.get(command) {
for handler in handlers {
handler(context.clone());
}
}
}
handler! {
contexts::Update,
after_update,
after_update_if,
}
handler! {
contexts::Animation,
animation,
animation_if,
}
handler! {
contexts::Audio,
audio,
audio_if,
}
handler! {
contexts::Update,
before_update,
before_update_if,
}
handler! {
contexts::ChosenInline,
chosen_inline,
chosen_inline_if,
}
handler! {
contexts::Contact,
contact,
contact_if,
}
handler! {
contexts::ConnectedWebsite,
connected_website,
connected_website_if,
}
handler! {
contexts::CreatedGroup,
created_group,
created_group_if,
}
handler! {
contexts::DataCallback,
data_callback,
data_callback_if,
}
handler! {
contexts::DeletedChatPhoto,
deleted_chat_photo,
deleted_chat_photo_if,
}
handler! {
contexts::Dice,
dice,
dice_if,
}
handler! {
contexts::Document,
document,
document_if,
}
handler! {
contexts::EditedAnimation,
edited_animation,
edited_animation_if,
}
handler! {
contexts::EditedAudio,
edited_audio,
edited_audio_if,
}
handler! {
contexts::EditedDocument,
edited_document,
edited_document_if,
}
handler! {
contexts::EditedLocation,
edited_location,
edited_location_if,
}
handler! {
contexts::EditedPhoto,
edited_photo,
edited_photo_if,
}
handler! {
contexts::EditedText,
edited_text,
edited_text_if,
}
handler! {
contexts::EditedVideo,
edited_video,
edited_video_if,
}
handler! {
contexts::GameCallback,
game_callback,
game_callback_if,
}
handler! {
contexts::Game,
game,
game_if,
}
handler! {
contexts::Inline,
inline,
inline_if,
}
handler! {
contexts::Invoice,
invoice,
invoice_if,
}
handler! {
contexts::LeftMember,
left_member,
left_member_if,
}
handler! {
contexts::Location,
location,
location_if,
}
handler! {
contexts::Migration,
migration,
migration_if,
}
handler! {
contexts::NewChatPhoto,
new_chat_photo,
new_chat_photo_if,
}
handler! {
contexts::NewChatTitle,
new_chat_title,
new_chat_title_if,
}
handler! {
contexts::NewMembers,
new_members,
new_members_if,
}
handler! {
contexts::Passport,
passport,
passport_if,
}
handler! {
contexts::Payment,
payment,
payment_if,
}
handler! {
contexts::Photo,
photo,
photo_if,
}
handler! {
contexts::PinnedMessage,
pinned_message,
pinned_message_if,
}
handler! {
contexts::Poll,
poll,
poll_if,
}
handler! {
contexts::PreCheckout,
pre_checkout,
pre_checkout_if,
}
handler! {
contexts::Shipping,
shipping,
shipping_if,
}
handler! {
contexts::Sticker,
sticker,
sticker_if,
}
handler! {
contexts::Text,
text,
text_if,
}
pub fn unhandled<H, F>(&mut self, handler: H)
where
H: (Fn(Arc<contexts::Unhandled>) -> F) + Send + Sync + 'static,
F: Future<Output = ()> + Send + 'static,
{
self.unhandled_handlers.push(Box::new(move |context| {
tokio::spawn(handler(context));
}))
}
pub fn unhandled_if<H, HF, P, PF>(&mut self, predicate: P, handler: H)
where
H: (Fn(Arc<contexts::Unhandled>) -> HF) + Send + Sync + 'static,
HF: Future<Output = ()> + Send + 'static,
P: (Fn(Arc<contexts::Unhandled>) -> PF) + Send + Sync + 'static,
PF: Future<Output = bool> + Send + 'static,
{
let predicate = Arc::new(predicate);
let handler = Arc::new(handler);
self.unhandled(move |context| {
let predicate = Arc::clone(&predicate);
let handler = Arc::clone(&handler);
async move {
if predicate(Arc::clone(&context)).await {
handler(context).await
}
}
});
}
fn will_handle_unhandled(&self) -> bool {
!self.unhandled_handlers.is_empty()
}
fn run_unhandled_handlers(&self, bot: Arc<Bot>, update: update::Kind) {
let context = Arc::new(contexts::Unhandled::new(bot, update));
for handler in &self.unhandled_handlers {
handler(context.clone());
}
}
handler! {
contexts::UpdatedPoll,
updated_poll,
updated_poll_if,
}
handler! {
contexts::PollAnswer,
poll_answer,
poll_answer_if,
}
handler! {
contexts::Venue,
venue,
venue_if,
}
handler! {
contexts::Video,
video,
video_if,
}
handler! {
contexts::VideoNote,
video_note,
video_note_if,
}
handler! {
contexts::Voice,
voice,
voice_if,
}
#[instrument(skip(self, bot, update))]
fn handle_update(&self, bot: Arc<Bot>, update: types::Update) {
trace!(update = debug(&update));
let update_context =
Arc::new(contexts::Update::new(Arc::clone(&bot), update.id));
self.run_before_update_handlers(update_context.clone());
match update.kind {
update::Kind::CallbackQuery(query) => match query.kind {
callback::Kind::Data(data)
if self.will_handle_data_callback() =>
{
let context = contexts::DataCallback::new(
bot,
query.id,
query.from,
query.origin,
query.chat_instance,
data,
);
self.run_data_callback_handlers(Arc::new(context));
}
callback::Kind::Game(game)
if self.will_handle_game_callback() =>
{
let context = contexts::GameCallback::new(
bot,
query.id,
query.from,
query.origin,
query.chat_instance,
game,
);
self.run_game_callback_handlers(Arc::new(context));
}
_ if self.will_handle_unhandled() => {
let update = update::Kind::CallbackQuery(query.clone());
self.run_unhandled_handlers(bot, update);
}
callback::Kind::Data(..) | callback::Kind::Game(..) => (),
},
update::Kind::ChosenInlineResult(result)
if self.will_handle_chosen_inline() =>
{
let context = contexts::ChosenInline::new(bot, result);
self.run_chosen_inline_handlers(Arc::new(context));
}
update::Kind::EditedMessage(message)
| update::Kind::EditedChannelPost(message) => {
self.handle_message_edit_update(bot, message);
}
update::Kind::InlineQuery(query) if self.will_handle_inline() => {
let context = contexts::Inline::new(bot, query);
self.run_inline_handlers(Arc::new(context));
}
update::Kind::Message(message)
| update::Kind::ChannelPost(message) => {
self.handle_message_update(bot, message);
}
update::Kind::PreCheckoutQuery(query)
if self.will_handle_pre_checkout() =>
{
let context = contexts::PreCheckout::new(bot, query);
self.run_pre_checkout_handlers(Arc::new(context));
}
update::Kind::Poll(poll) if self.will_handle_updated_poll() => {
let context = contexts::UpdatedPoll::new(bot, poll);
self.run_updated_poll_handlers(Arc::new(context));
}
update::Kind::PollAnswer(answer)
if self.will_handle_poll_answer() =>
{
let context = contexts::PollAnswer::new(bot, answer);
self.run_poll_answer_handlers(Arc::new(context));
}
update::Kind::ShippingQuery(query)
if self.will_handle_shipping() =>
{
let context = contexts::Shipping::new(bot, query);
self.run_shipping_handlers(Arc::new(context));
}
update if self.will_handle_unhandled() => {
self.run_unhandled_handlers(bot, update);
}
update::Kind::ChosenInlineResult(..)
| update::Kind::InlineQuery(..)
| update::Kind::Poll(..)
| update::Kind::PollAnswer(..)
| update::Kind::PreCheckoutQuery(..)
| update::Kind::ShippingQuery(..)
| update::Kind::Unknown => (),
}
self.run_after_update_handlers(update_context);
}
#[allow(clippy::cognitive_complexity)]
#[allow(clippy::too_many_lines)]
fn handle_message_update(&self, bot: Arc<Bot>, message: types::Message) {
let (data, kind) = message.split();
match kind {
message::Kind::Animation(animation, caption)
if self.will_handle_animation() =>
{
let context =
contexts::Animation::new(bot, data, *animation, caption);
self.run_animation_handlers(Arc::new(context));
}
message::Kind::Audio(audio, caption)
if self.will_handle_audio() =>
{
let context = contexts::Audio::new(bot, data, *audio, caption);
self.run_audio_handlers(Arc::new(context));
}
message::Kind::ChatPhotoDeleted
if self.will_handle_deleted_chat_photo() =>
{
let context = contexts::DeletedChatPhoto::new(bot, data);
self.run_deleted_chat_photo_handlers(Arc::new(context));
}
message::Kind::ConnectedWebsite(website)
if self.will_handle_connected_website() =>
{
let context =
contexts::ConnectedWebsite::new(bot, data, website);
self.run_connected_website_handlers(Arc::new(context));
}
message::Kind::Contact(contact) if self.will_handle_contact() => {
let context = contexts::Contact::new(bot, data, contact);
self.run_contact_handlers(Arc::new(context));
}
message::Kind::Dice(dice) if self.will_handle_dice() => {
let context = contexts::Dice::new(bot, data, dice);
self.run_dice_handlers(Arc::new(context));
}
message::Kind::Document(document, caption)
if self.will_handle_document() =>
{
let context =
contexts::Document::new(bot, data, *document, caption);
self.run_document_handlers(Arc::new(context));
}
message::Kind::Game(game) if self.will_handle_game() => {
let context = contexts::Game::new(bot, data, *game);
self.run_game_handlers(Arc::new(context));
}
message::Kind::GroupCreated if self.will_handle_created_group() => {
let context = contexts::CreatedGroup::new(bot, data);
self.run_created_group_handlers(Arc::new(context));
}
message::Kind::Invoice(invoice) if self.will_handle_invoice() => {
let context = contexts::Invoice::new(bot, data, invoice);
self.run_invoice_handlers(Arc::new(context));
}
message::Kind::LeftChatMember(member)
if self.will_handle_left_member() =>
{
let context = contexts::LeftMember::new(bot, data, member);
self.run_left_member_handlers(Arc::new(context));
}
message::Kind::Location(location)
if self.will_handle_location() =>
{
let context = contexts::Location::new(bot, data, location);
self.run_location_handlers(Arc::new(context));
}
message::Kind::MigrateFrom(old_id)
if self.will_handle_migration() =>
{
let context = contexts::Migration::new(bot, data, old_id);
self.run_migration_handlers(Arc::new(context));
}
message::Kind::MigrateTo(..) => (),
message::Kind::NewChatMembers(members)
if self.will_handle_new_members() =>
{
let context = contexts::NewMembers::new(bot, data, members);
self.run_new_members_handlers(Arc::new(context));
}
message::Kind::NewChatPhoto(photo)
if self.will_handle_new_chat_photo() =>
{
let context = contexts::NewChatPhoto::new(bot, data, photo);
self.run_new_chat_photo_handlers(Arc::new(context));
}
message::Kind::NewChatTitle(title)
if self.will_handle_new_chat_title() =>
{
let context = contexts::NewChatTitle::new(bot, data, title);
self.run_new_chat_title_handlers(Arc::new(context));
}
message::Kind::PassportData(passport_data)
if self.will_handle_passport() =>
{
let context = contexts::Passport::new(bot, data, passport_data);
self.run_passport_handlers(Arc::new(context));
}
message::Kind::Photo(photo, caption, media_group_id)
if self.will_handle_photo() =>
{
let context = contexts::Photo::new(
bot,
data,
photo,
caption,
media_group_id,
);
self.run_photo_handlers(Arc::new(context));
}
message::Kind::Pinned(message)
if self.will_handle_pinned_message() =>
{
let context = contexts::PinnedMessage::new(bot, data, *message);
self.run_pinned_message_handlers(Arc::new(context));
}
message::Kind::Poll(poll) if self.will_handle_poll() => {
let context = contexts::Poll::new(bot, data, poll);
self.run_poll_handlers(Arc::new(context));
}
message::Kind::Sticker(sticker) if self.will_handle_sticker() => {
let context = contexts::Sticker::new(bot, data, *sticker);
self.run_sticker_handlers(Arc::new(context));
}
message::Kind::SuccessfulPayment(payment)
if self.will_handle_payment() =>
{
let context = contexts::Payment::new(bot, data, *payment);
self.run_payment_handlers(Arc::new(context));
}
message::Kind::Text(text) if is_command(&text) => {
let (command, username) = parse_command(&text);
if !self.is_for_this_bot(username) {
return;
}
if self.will_handle_command(&command) {
let text = trim_command(text);
let context = contexts::Command::new(
command.clone(),
contexts::Text::new(bot, data, text),
);
self.run_command_handlers(&command, &Arc::new(context));
} else if self.will_handle_unhandled() {
let kind = message::Kind::Text(text);
let message = Message::new(data, kind);
let update = update::Kind::Message(message);
self.run_unhandled_handlers(bot, update);
}
}
message::Kind::Text(text) if self.will_handle_text() => {
let context = contexts::Text::new(bot, data, text);
self.run_text_handlers(Arc::new(context));
}
message::Kind::Venue(venue) if self.will_handle_venue() => {
let context = contexts::Venue::new(bot, data, venue);
self.run_venue_handlers(Arc::new(context));
}
message::Kind::Video(video, caption, media_group_id)
if self.will_handle_video() =>
{
let context = contexts::Video::new(
bot,
data,
*video,
caption,
media_group_id,
);
self.run_video_handlers(Arc::new(context));
}
message::Kind::VideoNote(video_note)
if self.will_handle_video_note() =>
{
let context = contexts::VideoNote::new(bot, data, video_note);
self.run_video_note_handlers(Arc::new(context));
}
message::Kind::Voice(voice, caption)
if self.will_handle_voice() =>
{
let context = contexts::Voice::new(bot, data, voice, caption);
self.run_voice_handlers(Arc::new(context));
}
message::Kind::SupergroupCreated
| message::Kind::ChannelCreated => {
warn!("Update not expected; skipping it")
}
kind if self.will_handle_unhandled() => {
let message = Message::new(data, kind);
let update = update::Kind::Message(message);
self.run_unhandled_handlers(bot, update);
}
message::Kind::Animation(..)
| message::Kind::Audio(..)
| message::Kind::ChatPhotoDeleted
| message::Kind::ConnectedWebsite(..)
| message::Kind::Contact(..)
| message::Kind::Dice(..)
| message::Kind::Document(..)
| message::Kind::Game(..)
| message::Kind::GroupCreated
| message::Kind::Invoice(..)
| message::Kind::LeftChatMember(..)
| message::Kind::Location(..)
| message::Kind::MigrateFrom(..)
| message::Kind::NewChatMembers(..)
| message::Kind::NewChatPhoto(..)
| message::Kind::NewChatTitle(..)
| message::Kind::PassportData(..)
| message::Kind::Photo(..)
| message::Kind::Pinned(..)
| message::Kind::Poll(..)
| message::Kind::Sticker(..)
| message::Kind::SuccessfulPayment(..)
| message::Kind::Text(..)
| message::Kind::Venue(..)
| message::Kind::Video(..)
| message::Kind::VideoNote(..)
| message::Kind::Voice(..)
| message::Kind::Unknown => (),
}
}
#[allow(clippy::too_many_lines)]
fn handle_message_edit_update(
&self,
bot: Arc<Bot>,
message: types::Message,
) {
let (data, kind) = message.split();
let edit_date = if let Some(edit_date) = data.edit_date {
edit_date
} else {
error!("No `edit_date` on an edited message; skipping it");
return;
};
match kind {
message::Kind::Animation(animation, caption)
if self.will_handle_edited_animation() =>
{
let context = contexts::EditedAnimation::new(
bot, data, edit_date, *animation, caption,
);
self.run_edited_animation_handlers(Arc::new(context));
}
message::Kind::Audio(audio, caption)
if self.will_handle_edited_audio() =>
{
let context = contexts::EditedAudio::new(
bot, data, edit_date, *audio, caption,
);
self.run_edited_audio_handlers(Arc::new(context));
}
message::Kind::Document(document, caption)
if self.will_handle_edited_document() =>
{
let context = contexts::EditedDocument::new(
bot, data, edit_date, *document, caption,
);
self.run_edited_document_handlers(Arc::new(context));
}
message::Kind::Location(location)
if self.will_handle_edited_location() =>
{
let context = contexts::EditedLocation::new(
bot, data, edit_date, location,
);
self.run_edited_location_handlers(Arc::new(context));
}
message::Kind::Photo(photo, caption, media_group_id)
if self.will_handle_edited_photo() =>
{
let context = contexts::EditedPhoto::new(
bot,
data,
edit_date,
photo,
caption,
media_group_id,
);
self.run_edited_photo_handlers(Arc::new(context));
}
message::Kind::Text(text) if is_command(&text) => {
let (command, username) = parse_command(&text);
if !self.is_for_this_bot(username) {
return;
}
if self.will_handle_edited_command(&command) {
let text = trim_command(text);
let context = contexts::Command::new(
command.clone(),
contexts::EditedText::new(bot, data, edit_date, text),
);
self.run_edited_command_handlers(
&command,
&Arc::new(context),
);
} else if self.will_handle_unhandled() {
let kind = message::Kind::Text(text);
let message = Message::new(data, kind);
let update = update::Kind::EditedMessage(message);
self.run_unhandled_handlers(bot, update);
}
}
message::Kind::Text(text) if self.will_handle_edited_text() => {
let context =
contexts::EditedText::new(bot, data, edit_date, text);
self.run_edited_text_handlers(Arc::new(context));
}
message::Kind::Video(video, caption, media_group_id)
if self.will_handle_edited_video() =>
{
let context = contexts::EditedVideo::new(
bot,
data,
edit_date,
*video,
caption,
media_group_id,
);
self.run_edited_video_handlers(Arc::new(context));
}
message::Kind::Contact(..)
| message::Kind::Dice(..)
| message::Kind::Game(..)
| message::Kind::Invoice(..)
| message::Kind::Poll(..)
| message::Kind::Sticker(..)
| message::Kind::Venue(..)
| message::Kind::VideoNote(..)
| message::Kind::Voice(..)
| message::Kind::ChannelCreated
| message::Kind::ChatPhotoDeleted
| message::Kind::ConnectedWebsite(..)
| message::Kind::GroupCreated
| message::Kind::LeftChatMember(..)
| message::Kind::MigrateFrom(..)
| message::Kind::MigrateTo(..)
| message::Kind::NewChatMembers(..)
| message::Kind::NewChatPhoto(..)
| message::Kind::NewChatTitle(..)
| message::Kind::PassportData(..)
| message::Kind::Pinned(..)
| message::Kind::SuccessfulPayment(..)
| message::Kind::SupergroupCreated => warn!(
"Unexpected message kind received as an edited message; \
skipping it"
),
kind if self.will_handle_unhandled() => {
let message = Message::new(data, kind);
let update = update::Kind::EditedMessage(message);
self.run_unhandled_handlers(bot, update)
}
message::Kind::Animation(..)
| message::Kind::Audio(..)
| message::Kind::Document(..)
| message::Kind::Location(..)
| message::Kind::Photo(..)
| message::Kind::Text(..)
| message::Kind::Video(..)
| message::Kind::Unknown => (),
}
}
fn is_for_this_bot(&self, username: Option<&str>) -> bool {
if let Some(username) = username {
self.username.as_ref().map(|x| x == username) == Some(true)
} else {
true
}
}
pub async fn fetch_username(&mut self) -> Result<(), errors::MethodCall> {
let me = self.bot.get_me().call().await?;
let username = me
.user
.username
.expect("[tbot] Expected the bot to have a username");
self.username(username);
Ok(())
}
}
fn is_command(text: &Text) -> bool {
text.entities.get(0).map(|entity| {
entity.kind == EntityKind::BotCommand && entity.offset == 0
}) == Some(true)
}
fn parse_command(text: &Text) -> (String, Option<&str>) {
let mut iter =
text.value.split_whitespace().next().unwrap()[1..].split('@');
let command = iter.next().unwrap();
let username = iter.next();
(command.to_string(), username)
}
fn trim_command(text: Text) -> Text {
let mut entities = text.entities.into_iter();
let command_entity = entities.next().unwrap();
let old_length = text.value.chars().count();
let value: String = text
.value
.chars()
.skip(command_entity.length)
.skip_while(|x| x.is_whitespace())
.collect();
let new_length = value.chars().count();
let entities = entities
.map(|entity| Entity {
kind: entity.kind,
length: entity.length,
offset: entity.offset - (old_length - new_length),
})
.collect();
Text { value, entities }
}