mirror of
https://github.com/teloxide/teloxide.git
synced 2025-03-14 11:44:04 +01:00
Merge branch 'dev' into cows_for_form_builder
This commit is contained in:
commit
06c44831b6
81 changed files with 2947 additions and 1000 deletions
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "async-telegram-bot"
|
||||
name = "telebofr"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 async-telegram-bot
|
||||
Copyright (c) 2019 telebofr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
22
README.md
22
README.md
|
@ -1,30 +1,20 @@
|
|||
|
||||
<div align="center">
|
||||
<h1>async-telegram-bot</h1>
|
||||
<img src="ICON.png" width="200"/>
|
||||
<h1>telebofr</h1>
|
||||
|
||||
<a href="https://docs.rs/async-telegram-bot/">
|
||||
<a href="https://docs.rs/telebofr/">
|
||||
<img src="https://img.shields.io/badge/docs.rs-link-blue.svg">
|
||||
</a>
|
||||
<a href="https://travis-ci.com/async-telegram-bot/async-telegram-bot">
|
||||
<img src="https://travis-ci.com/async-telegram-bot/async-telegram-bot.svg?branch=dev" />
|
||||
<a href="https://travis-ci.com/telebofr/telebofr">
|
||||
<img src="https://travis-ci.com/telebofr/telebofr.svg?branch=dev" />
|
||||
</a>
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg">
|
||||
</a>
|
||||
<a href="https://crates.io/crates/async-telegram-bot">
|
||||
<a href="https://crates.io/crates/telebofr">
|
||||
<img src="https://img.shields.io/badge/crates.io-v0.1.0-orange.svg">
|
||||
</a>
|
||||
|
||||
<br>
|
||||
<img src="ICON.png" width="300"/>
|
||||
<br>
|
||||
|
||||
A full-featured framework that empowers you to easily build [Telegram bots](https://telegram.org/blog/bot-revolution) using the [`async`/`.await`](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html) syntax in [Rust](https://www.rust-lang.org/). It handles all the difficult stuff so you can focus only on your business logic.
|
||||
</div>
|
||||
|
||||
## A simple bot
|
||||
```rust
|
||||
fn main() {
|
||||
let bot = Bot::new(API_TOKEN).bla().bla();
|
||||
}
|
||||
```
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
format_code_in_doc_comments = true
|
||||
wrap_comments = true
|
||||
format_strings = true
|
||||
max_width = 80
|
||||
max_width = 80
|
||||
merge_imports = true
|
217
src/bot/api.rs
217
src/bot/api.rs
|
@ -1,24 +1,28 @@
|
|||
use crate::{
|
||||
bot::Bot,
|
||||
requests::{
|
||||
AnswerPreCheckoutQuery, AnswerShippingQuery, EditMessageLiveLocation,
|
||||
ForwardMessage, GetFile, GetMe, KickChatMember, PinChatMessage,
|
||||
PromoteChatMember, RestrictChatMember, SendAudio, SendChatAction,
|
||||
SendContact, SendLocation, SendMediaGroup, SendMessage, SendPhoto,
|
||||
SendPoll, SendVenue, SendVideoNote, SendVoice, StopMessageLiveLocation,
|
||||
UnbanChatMember, UnpinChatMessage, GetUpdates
|
||||
AnswerCallbackQuery, AnswerPreCheckoutQuery, AnswerShippingQuery,
|
||||
DeleteChatPhoto, DeleteChatStickerSet, EditMessageLiveLocation,
|
||||
ExportCharInviteLink, ForwardMessage, GetChat, GetChatAdministrators,
|
||||
GetChatMember, GetChatMembersCount, GetFile, GetMe, GetUpdates,
|
||||
KickChatMember, LeaveChat, PinChatMessage, PromoteChatMember,
|
||||
RestrictChatMember, SendAnimation, SendAudio, SendChatAction,
|
||||
SendContact, SendDocument, SendLocation, SendMediaGroup, SendMessage,
|
||||
SendPhoto, SendPoll, SendVenue, SendVideo, SendVideoNote, SendVoice,
|
||||
SetChatDescription, SetChatPermissions, SetChatPhoto,
|
||||
SetChatStickerSet, SetChatTitle, StopMessageLiveLocation,
|
||||
UnbanChatMember, UnpinChatMessage,
|
||||
},
|
||||
types::{ChatAction, ChatId, ChatPermissions, InputFile, InputMedia},
|
||||
};
|
||||
|
||||
/// Telegram functions
|
||||
impl Bot {
|
||||
pub fn get_me(&self) -> GetMe {
|
||||
GetMe::new(self.ctx())
|
||||
GetMe::new(self)
|
||||
}
|
||||
|
||||
pub fn get_updates(&self) -> GetUpdates {
|
||||
GetUpdates::new(self.ctx())
|
||||
GetUpdates::new(self)
|
||||
}
|
||||
|
||||
pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage
|
||||
|
@ -26,7 +30,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
T: Into<String>,
|
||||
{
|
||||
SendMessage::new(self.ctx(), chat_id, text)
|
||||
SendMessage::new(self, chat_id, text)
|
||||
}
|
||||
|
||||
pub fn edit_message_live_location<Lt, Lg>(
|
||||
|
@ -38,7 +42,7 @@ impl Bot {
|
|||
Lt: Into<f64>,
|
||||
Lg: Into<f64>,
|
||||
{
|
||||
EditMessageLiveLocation::new(self.ctx(), latitude, longitude)
|
||||
EditMessageLiveLocation::new(self, latitude, longitude)
|
||||
}
|
||||
|
||||
pub fn forward_message<C, F, M>(
|
||||
|
@ -52,7 +56,7 @@ impl Bot {
|
|||
F: Into<ChatId>,
|
||||
M: Into<i32>,
|
||||
{
|
||||
ForwardMessage::new(self.ctx(), chat_id, from_chat_id, message_id)
|
||||
ForwardMessage::new(self, chat_id, from_chat_id, message_id)
|
||||
}
|
||||
|
||||
pub fn send_audio<C, A>(&self, chat_id: C, audio: A) -> SendAudio
|
||||
|
@ -60,7 +64,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
A: Into<InputFile>,
|
||||
{
|
||||
SendAudio::new(self.ctx(), chat_id, audio)
|
||||
SendAudio::new(self, chat_id, audio)
|
||||
}
|
||||
|
||||
pub fn send_location<C, Lt, Lg>(
|
||||
|
@ -74,7 +78,7 @@ impl Bot {
|
|||
Lt: Into<f64>,
|
||||
Lg: Into<f64>,
|
||||
{
|
||||
SendLocation::new(self.ctx(), chat_id, latitude, longitude)
|
||||
SendLocation::new(self, chat_id, latitude, longitude)
|
||||
}
|
||||
|
||||
pub fn send_media_group<C, M>(&self, chat_id: C, media: M) -> SendMediaGroup
|
||||
|
@ -82,7 +86,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
M: Into<Vec<InputMedia>>,
|
||||
{
|
||||
SendMediaGroup::new(self.ctx(), chat_id, media)
|
||||
SendMediaGroup::new(self, chat_id, media)
|
||||
}
|
||||
|
||||
pub fn send_photo<C, P>(&self, chat_id: C, photo: P) -> SendPhoto
|
||||
|
@ -90,18 +94,18 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
P: Into<InputFile>,
|
||||
{
|
||||
SendPhoto::new(self.ctx(), chat_id, photo)
|
||||
SendPhoto::new(self, chat_id, photo)
|
||||
}
|
||||
|
||||
pub fn stop_message_live_location(&self) -> StopMessageLiveLocation {
|
||||
StopMessageLiveLocation::new(self.ctx())
|
||||
StopMessageLiveLocation::new(self)
|
||||
}
|
||||
|
||||
pub fn get_file<F>(&self, file_id: F) -> GetFile
|
||||
where
|
||||
F: Into<String>,
|
||||
{
|
||||
GetFile::new(self.ctx(), file_id)
|
||||
GetFile::new(self, file_id)
|
||||
}
|
||||
|
||||
pub fn answer_pre_checkout_query<I, O>(
|
||||
|
@ -113,7 +117,14 @@ impl Bot {
|
|||
I: Into<String>,
|
||||
O: Into<bool>,
|
||||
{
|
||||
AnswerPreCheckoutQuery::new(self.ctx(), pre_checkout_query_id, ok)
|
||||
AnswerPreCheckoutQuery::new(self, pre_checkout_query_id, ok)
|
||||
}
|
||||
|
||||
pub fn get_chat<I>(&self, chat_id: I) -> GetChat
|
||||
where
|
||||
I: Into<ChatId>,
|
||||
{
|
||||
GetChat::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn answer_shipping_query<I, O>(
|
||||
|
@ -125,7 +136,7 @@ impl Bot {
|
|||
I: Into<String>,
|
||||
O: Into<bool>,
|
||||
{
|
||||
AnswerShippingQuery::new(self.ctx(), shipping_query_id, ok)
|
||||
AnswerShippingQuery::new(self, shipping_query_id, ok)
|
||||
}
|
||||
|
||||
pub fn kick_chat_member<C, U>(
|
||||
|
@ -137,7 +148,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
KickChatMember::new(self.ctx(), chat_id, user_id)
|
||||
KickChatMember::new(self, chat_id, user_id)
|
||||
}
|
||||
|
||||
pub fn pin_chat_message<C, M>(
|
||||
|
@ -149,7 +160,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
M: Into<i32>,
|
||||
{
|
||||
PinChatMessage::new(self.ctx(), chat_id, message_id)
|
||||
PinChatMessage::new(self, chat_id, message_id)
|
||||
}
|
||||
|
||||
pub fn promote_chat_member<C, U>(
|
||||
|
@ -161,7 +172,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
PromoteChatMember::new(self.ctx(), chat_id, user_id)
|
||||
PromoteChatMember::new(self, chat_id, user_id)
|
||||
}
|
||||
|
||||
pub fn restrict_chat_member<C, U, P>(
|
||||
|
@ -175,7 +186,7 @@ impl Bot {
|
|||
U: Into<i32>,
|
||||
P: Into<ChatPermissions>,
|
||||
{
|
||||
RestrictChatMember::new(self.ctx(), chat_id, user_id, permissions)
|
||||
RestrictChatMember::new(self, chat_id, user_id, permissions)
|
||||
}
|
||||
|
||||
pub fn send_chat_action<C, A>(
|
||||
|
@ -187,7 +198,7 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
A: Into<ChatAction>,
|
||||
{
|
||||
SendChatAction::new(self.ctx(), chat_id, action)
|
||||
SendChatAction::new(self, chat_id, action)
|
||||
}
|
||||
|
||||
pub fn send_contact<C, P, F>(
|
||||
|
@ -201,7 +212,7 @@ impl Bot {
|
|||
P: Into<String>,
|
||||
F: Into<String>,
|
||||
{
|
||||
SendContact::new(self.ctx(), chat_id, phone_number, first_name)
|
||||
SendContact::new(self, chat_id, phone_number, first_name)
|
||||
}
|
||||
|
||||
pub fn send_poll<C, Q, O>(
|
||||
|
@ -215,7 +226,7 @@ impl Bot {
|
|||
Q: Into<String>,
|
||||
O: Into<Vec<String>>,
|
||||
{
|
||||
SendPoll::new(self.ctx(), chat_id, question, options)
|
||||
SendPoll::new(self, chat_id, question, options)
|
||||
}
|
||||
|
||||
pub fn send_venue<C, Lt, Lg, T, A>(
|
||||
|
@ -233,7 +244,7 @@ impl Bot {
|
|||
T: Into<String>,
|
||||
A: Into<String>,
|
||||
{
|
||||
SendVenue::new(self.ctx(), chat_id, latitude, longitude, title, address)
|
||||
SendVenue::new(self, chat_id, latitude, longitude, title, address)
|
||||
}
|
||||
|
||||
pub fn send_video_note<C, V>(
|
||||
|
@ -243,17 +254,24 @@ impl Bot {
|
|||
) -> SendVideoNote
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<String>, // TODO: InputFile
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
SendVideoNote::new(self.ctx(), chat_id, video_note)
|
||||
SendVideoNote::new(self, chat_id, video_note)
|
||||
}
|
||||
|
||||
pub fn send_voice<C, V>(&self, chat_id: C, voice: V) -> SendVoice
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<String>, // TODO: InputFile
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
SendVoice::new(self.ctx(), chat_id, voice)
|
||||
SendVoice::new(self, chat_id, voice)
|
||||
}
|
||||
|
||||
pub fn send_chat_description<C>(&self, chat_id: C) -> SetChatDescription
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
SetChatDescription::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn unban_chat_member<C, U>(
|
||||
|
@ -265,13 +283,144 @@ impl Bot {
|
|||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
UnbanChatMember::new(self.ctx(), chat_id, user_id)
|
||||
UnbanChatMember::new(self, chat_id, user_id)
|
||||
}
|
||||
|
||||
pub fn unpin_chat_message<C>(&self, chat_id: C) -> UnpinChatMessage
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
UnpinChatMessage::new(self.ctx(), chat_id)
|
||||
UnpinChatMessage::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn answer_callback_query<S>(
|
||||
&self,
|
||||
callback_query_id: S,
|
||||
) -> AnswerCallbackQuery
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
AnswerCallbackQuery::new(self, callback_query_id)
|
||||
}
|
||||
|
||||
pub fn delete_chat_sticker_set<C>(&self, chat_id: C) -> DeleteChatStickerSet
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
DeleteChatStickerSet::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn set_chat_sticker_set<C, S>(
|
||||
&self,
|
||||
chat_id: C,
|
||||
sticker_set_name: S,
|
||||
) -> SetChatStickerSet
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
S: Into<String>,
|
||||
{
|
||||
SetChatStickerSet::new(self, chat_id, sticker_set_name)
|
||||
}
|
||||
|
||||
pub fn get_chat_member<C, I>(&self, chat_id: C, user_id: I) -> GetChatMember
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
I: Into<i32>,
|
||||
{
|
||||
GetChatMember::new(self, chat_id, user_id)
|
||||
}
|
||||
|
||||
pub fn get_chat_administrators<C, I>(
|
||||
&self,
|
||||
chat_id: C,
|
||||
) -> GetChatAdministrators
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
GetChatAdministrators::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn get_chat_members_count<C>(&self, chat_id: C) -> GetChatMembersCount
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
GetChatMembersCount::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn send_video<C, V>(&self, chat_id: C, video: V) -> SendVideo
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
SendVideo::new(self, chat_id, video)
|
||||
}
|
||||
|
||||
pub fn send_document<C, D>(&self, chat_id: C, document: D) -> SendDocument
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
D: Into<InputFile>,
|
||||
{
|
||||
SendDocument::new(self, chat_id, document)
|
||||
}
|
||||
|
||||
pub fn send_animation<C, S>(
|
||||
&self,
|
||||
chat_id: C,
|
||||
animation: S,
|
||||
) -> SendAnimation
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
S: Into<InputFile>,
|
||||
{
|
||||
SendAnimation::new(self, chat_id, animation)
|
||||
}
|
||||
|
||||
pub fn set_chat_title<C, T>(&self, chat_id: C, title: T) -> SetChatTitle
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
T: Into<String>,
|
||||
{
|
||||
SetChatTitle::new(self, chat_id, title)
|
||||
}
|
||||
|
||||
pub fn delete_chat_photo<C>(&self, chat_id: C) -> DeleteChatPhoto
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
DeleteChatPhoto::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn leave_chat<C>(&self, chat_id: C) -> LeaveChat
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
LeaveChat::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn set_chat_photo<C, P>(&self, chat_id: C, photo: P) -> SetChatPhoto
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
P: Into<InputFile>,
|
||||
{
|
||||
SetChatPhoto::new(self, chat_id, photo)
|
||||
}
|
||||
|
||||
pub fn export_chat_invite_link<C>(&self, chat_id: C) -> ExportCharInviteLink
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
ExportCharInviteLink::new(self, chat_id)
|
||||
}
|
||||
|
||||
pub fn set_chat_permissions<C, CP>(
|
||||
&self,
|
||||
chat_id: C,
|
||||
permissions: CP,
|
||||
) -> SetChatPermissions
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
CP: Into<ChatPermissions>,
|
||||
{
|
||||
SetChatPermissions::new(self, chat_id, permissions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::network::download_file_stream;
|
|||
use crate::{bot::Bot, network::download_file, DownloadError};
|
||||
|
||||
impl Bot {
|
||||
/// Download file from telegram into `destination`.
|
||||
/// Download a file from Telegram into `destination`.
|
||||
/// `path` can be obtained from [`get_file`] method.
|
||||
///
|
||||
/// For downloading as Stream of Chunks see [`download_file_stream`].
|
||||
|
@ -16,11 +16,10 @@ impl Bot {
|
|||
/// ## Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use async_telegram_bot::{
|
||||
/// bot::Bot, requests::Request, types::File as TgFile,
|
||||
/// };
|
||||
/// use telebofr::types::File as TgFile;
|
||||
/// use tokio::fs::File;
|
||||
/// # use async_telegram_bot::RequestError;
|
||||
/// # use telebofr::RequestError;
|
||||
/// use telebofr::Bot;
|
||||
///
|
||||
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let bot = Bot::new("TOKEN");
|
||||
|
@ -44,9 +43,9 @@ impl Bot {
|
|||
download_file(&self.client, &self.token, path, destination).await
|
||||
}
|
||||
|
||||
/// Download file from telegram.
|
||||
/// Download a file from Telegram.
|
||||
///
|
||||
/// `path` can be obtained from [`get_file`] method.
|
||||
/// `path` can be obtained from the [`get_file`] method.
|
||||
///
|
||||
/// For downloading into [`AsyncWrite`] (e.g. [`tokio::fs::File`])
|
||||
/// see [`download_file`].
|
||||
|
|
|
@ -1,39 +1,45 @@
|
|||
//! A Telegram bot.
|
||||
|
||||
use reqwest::Client;
|
||||
|
||||
use crate::requests::RequestContext;
|
||||
|
||||
mod api;
|
||||
mod download;
|
||||
|
||||
/// A Telegram bot used to build requests.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bot {
|
||||
token: String,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
/// Constructors
|
||||
impl Bot {
|
||||
pub fn new(token: &str) -> Self {
|
||||
pub fn new<S>(token: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Bot {
|
||||
token: String::from(token),
|
||||
token: token.into(),
|
||||
client: Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_client(token: &str, client: Client) -> Self {
|
||||
pub fn with_client<S>(token: S, client: Client) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Bot {
|
||||
token: String::from(token),
|
||||
token: token.into(),
|
||||
client,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bot {
|
||||
fn ctx(&self) -> RequestContext {
|
||||
RequestContext {
|
||||
token: &self.token,
|
||||
client: &self.client,
|
||||
}
|
||||
#[inline]
|
||||
pub fn token(&self) -> &str {
|
||||
&self.token
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn client(&self) -> &Client {
|
||||
&self.client
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
//! Update dispatching.
|
||||
|
||||
pub mod filter;
|
||||
pub mod handler;
|
||||
pub mod simple;
|
||||
pub mod updater;
|
||||
|
||||
pub use filter::Filter;
|
||||
pub use handler::Handler;
|
|
@ -1,304 +0,0 @@
|
|||
pub mod error_policy;
|
||||
|
||||
use crate::{
|
||||
dispatcher::{
|
||||
filter::Filter,
|
||||
handler::Handler,
|
||||
updater::Updater,
|
||||
},
|
||||
types::{
|
||||
Update,
|
||||
Message,
|
||||
UpdateKind,
|
||||
CallbackQuery,
|
||||
ChosenInlineResult,
|
||||
},
|
||||
};
|
||||
|
||||
use futures::StreamExt;
|
||||
use crate::dispatcher::simple::error_policy::ErrorPolicy;
|
||||
|
||||
|
||||
type Handlers<'a, T, E> = Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E> + 'a>)>;
|
||||
|
||||
/// Dispatcher that dispatches updates from telegram.
|
||||
///
|
||||
/// This is 'simple' implementation with following limitations:
|
||||
/// - Error (`E` generic parameter) _must_ implement [`std::fmt::Debug`]
|
||||
/// - All 'handlers' are boxed
|
||||
/// - Handler's fututres are also boxed
|
||||
/// - [Custom error policy] is also boxed
|
||||
/// - All errors from [updater] are ignored (TODO: remove this limitation)
|
||||
/// - All handlers executed in order (this means that in dispatcher have
|
||||
/// 2 upadtes it will first execute some handler into complition with
|
||||
/// first update and **then** search for handler for second update,
|
||||
/// this is probably wrong)
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// Simplest example:
|
||||
/// ```no_run
|
||||
/// # async fn run() {
|
||||
/// use std::convert::Infallible;
|
||||
/// use async_telegram_bot::{
|
||||
/// bot::Bot,
|
||||
/// types::Message,
|
||||
/// dispatcher::{
|
||||
/// updater::polling,
|
||||
/// simple::{Dispatcher, error_policy::ErrorPolicy},
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// async fn handle_edited_message(mes: Message) {
|
||||
/// println!("Edited message: {:?}", mes)
|
||||
/// }
|
||||
///
|
||||
/// let bot = Bot::new("TOKEN");
|
||||
///
|
||||
/// // create dispatcher which handlers can't fail
|
||||
/// // with error policy that just ignores all errors (that can't ever happen)
|
||||
/// let mut dp = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore)
|
||||
/// // Add 'handler' that will handle all messages sent to the bot
|
||||
/// .message_handler(true, |mes: Message| async move {
|
||||
/// println!("New message: {:?}", mes)
|
||||
/// })
|
||||
/// // Add 'handler' that will handle all
|
||||
/// // messages edited in chat with the bot
|
||||
/// .edited_message_handler(true, handle_edited_message);
|
||||
///
|
||||
/// // Start dispatching updates from long polling
|
||||
/// dp.dispatch(polling(&bot)).await;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`std::fmt::Debug`]: std::fmt::Debug
|
||||
/// [Custom error policy]: crate::dispatcher::simple::error_policy::ErrorPolicy::Custom
|
||||
/// [updater]: crate::dispatcher::updater
|
||||
pub struct Dispatcher<'a, E> {
|
||||
message_handlers: Handlers<'a, Message, E>,
|
||||
edited_message_handlers: Handlers<'a, Message, E>,
|
||||
channel_post_handlers: Handlers<'a, Message, E>,
|
||||
edited_channel_post_handlers: Handlers<'a, Message, E>,
|
||||
inline_query_handlers: Handlers<'a, (), E>,
|
||||
chosen_inline_result_handlers: Handlers<'a, ChosenInlineResult, E>,
|
||||
callback_query_handlers: Handlers<'a, CallbackQuery, E>,
|
||||
error_policy: ErrorPolicy<'a, E>,
|
||||
}
|
||||
|
||||
impl<'a, E> Dispatcher<'a, E>
|
||||
where
|
||||
E: std::fmt::Debug, // TODO: Is this really necessary?
|
||||
{
|
||||
pub fn new(error_policy: ErrorPolicy<'a, E>) -> Self {
|
||||
Dispatcher {
|
||||
message_handlers: Vec::new(),
|
||||
edited_message_handlers: Vec::new(),
|
||||
channel_post_handlers: Vec::new(),
|
||||
edited_channel_post_handlers: Vec::new(),
|
||||
inline_query_handlers: Vec::new(),
|
||||
chosen_inline_result_handlers: Vec::new(),
|
||||
callback_query_handlers: Vec::new(),
|
||||
error_policy
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.message_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn edited_message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.edited_message_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn channel_post_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.channel_post_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn edited_channel_post_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.edited_channel_post_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inline_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<()> + 'a,
|
||||
H: Handler<'a, (), E> + 'a,
|
||||
{
|
||||
self.inline_query_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn chosen_inline_result_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<ChosenInlineResult> + 'a,
|
||||
H: Handler<'a, ChosenInlineResult, E> + 'a,
|
||||
{
|
||||
self.chosen_inline_result_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn callback_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<CallbackQuery> + 'a,
|
||||
H: Handler<'a, CallbackQuery, E> + 'a,
|
||||
{
|
||||
self.callback_query_handlers.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: Can someone simplify this?
|
||||
pub async fn dispatch<U, UE>(&mut self, updates: U)
|
||||
where
|
||||
U: Updater<UE> + 'a
|
||||
{
|
||||
updates.for_each(|res| {
|
||||
async {
|
||||
let res = res;
|
||||
let Update { kind, id } = match res {
|
||||
Ok(upd) => upd,
|
||||
_ => return // TODO: proper error handling
|
||||
};
|
||||
|
||||
log::debug!("Handled update#{id:?}: {kind:?}", id = id, kind = kind);
|
||||
|
||||
// TODO: can someone extract this to a function?
|
||||
macro_rules! call {
|
||||
($h:expr, $value:expr) => {{
|
||||
let value = $value;
|
||||
let handler = $h.iter().find_map(|e| {
|
||||
let (filter, handler) = e;
|
||||
if filter.test(&value) {
|
||||
Some(handler)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
match handler {
|
||||
Some(handler) => {
|
||||
if let Err(err) = handler.handle(value).await {
|
||||
self.error_policy.handle_error(err).await;
|
||||
}
|
||||
},
|
||||
None => log::warn!("Unhandled update: {:?}", value)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
match kind {
|
||||
UpdateKind::Message(mes) => call!(self.message_handlers, mes),
|
||||
UpdateKind::EditedMessage(mes) => call!(self.edited_message_handlers, mes),
|
||||
UpdateKind::ChannelPost(post) => call!(self.channel_post_handlers, post),
|
||||
UpdateKind::EditedChannelPost(post) => call!(self.edited_channel_post_handlers, post),
|
||||
UpdateKind::InlineQuery(query) => call!(self.inline_query_handlers, query),
|
||||
UpdateKind::ChosenInlineResult(result) => call!(self.chosen_inline_result_handlers, result),
|
||||
UpdateKind::CallbackQuery(callback) => call!(self.callback_query_handlers, callback),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::Infallible;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use crate::{
|
||||
types::{
|
||||
Message, ChatKind, MessageKind, Sender, ForwardKind, MediaKind, Chat, User, Update, UpdateKind
|
||||
},
|
||||
dispatcher::{simple::{Dispatcher, error_policy::ErrorPolicy}, updater::StreamUpdater},
|
||||
};
|
||||
use futures::Stream;
|
||||
|
||||
#[tokio::test]
|
||||
async fn first_handler_executes_1_time() {
|
||||
let counter = &AtomicI32::new(0);
|
||||
let counter2 = &AtomicI32::new(0);
|
||||
|
||||
let mut dp = Dispatcher::<Infallible>::new(ErrorPolicy::Ignore)
|
||||
.message_handler(true, |_mes: Message| async move {
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
})
|
||||
.message_handler(true, |_mes: Message| async move {
|
||||
counter2.fetch_add(1, Ordering::SeqCst);
|
||||
Ok::<_, Infallible>(())
|
||||
});
|
||||
|
||||
dp.dispatch(one_message_updater()).await;
|
||||
|
||||
assert_eq!(counter.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(counter2.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
|
||||
fn message() -> Message {
|
||||
Message {
|
||||
id: 6534,
|
||||
date: 1567898953,
|
||||
chat: Chat {
|
||||
id: 218485655,
|
||||
photo: None,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
first_name: Some("W".to_string()),
|
||||
last_name: None,
|
||||
username: Some("WaffleLapkin".to_string()),
|
||||
},
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 457569668,
|
||||
is_bot: true,
|
||||
first_name: "BT".to_string(),
|
||||
last_name: None,
|
||||
username: Some("BloodyTestBot".to_string()),
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Text {
|
||||
text: "text".to_string(),
|
||||
entities: vec![],
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn message_update() -> Update {
|
||||
Update { id: 0, kind: UpdateKind::Message(message()) }
|
||||
}
|
||||
|
||||
fn one_message_updater() -> StreamUpdater<impl Stream<Item=Result<Update, Infallible>>> {
|
||||
use futures::future::ready;
|
||||
use futures::stream;
|
||||
|
||||
StreamUpdater::new(
|
||||
stream::once(ready(Ok(message_update())))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
use std::fmt::Debug;
|
||||
use std::{fmt::Debug, future::Future, pin::Pin};
|
||||
|
||||
// TODO: shouldn't it be trait?
|
||||
pub enum ErrorPolicy<'a, E> {
|
||||
|
@ -15,14 +13,12 @@ where
|
|||
{
|
||||
pub async fn handle_error(&self, error: E) {
|
||||
match self {
|
||||
Self::Ignore => {},
|
||||
Self::Ignore => {}
|
||||
Self::Log => {
|
||||
// TODO: better message
|
||||
log::error!("Error in handler: {:?}", error)
|
||||
}
|
||||
Self::Custom(func) => {
|
||||
func(error).await
|
||||
}
|
||||
Self::Custom(func) => func(error).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,4 +29,4 @@ where
|
|||
{
|
||||
Self::Custom(Box::new(move |e| Box::pin(f(e))))
|
||||
}
|
||||
}
|
||||
}
|
373
src/dispatching/dispatchers/filter/mod.rs
Normal file
373
src/dispatching/dispatchers/filter/mod.rs
Normal file
|
@ -0,0 +1,373 @@
|
|||
use futures::StreamExt;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
dispatching::{
|
||||
dispatchers::filter::error_policy::ErrorPolicy, filters::Filter,
|
||||
handler::Handler, updater::Updater, Dispatcher,
|
||||
},
|
||||
types::{CallbackQuery, ChosenInlineResult, Message, Update, UpdateKind},
|
||||
};
|
||||
|
||||
pub mod error_policy;
|
||||
|
||||
type Handlers<'a, T, E> =
|
||||
Vec<(Box<dyn Filter<T> + 'a>, Box<dyn Handler<'a, T, E> + 'a>)>;
|
||||
|
||||
/// Dispatcher that dispatches updates from telegram.
|
||||
///
|
||||
/// This is 'filter' implementation with following limitations:
|
||||
/// - Error (`E` generic parameter) _must_ implement [`std::fmt::Debug`]
|
||||
/// - All 'handlers' are boxed
|
||||
/// - Handler's fututres are also boxed
|
||||
/// - [Custom error policy] is also boxed
|
||||
/// - All errors from [updater] are ignored (TODO: remove this limitation)
|
||||
/// - All handlers executed in order (this means that in dispatching have 2
|
||||
/// upadtes it will first execute some handler into complition with first
|
||||
/// update and **then** search for handler for second update, this is probably
|
||||
/// wrong)
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// Simplest example:
|
||||
/// ```no_run
|
||||
/// # use telebofr::Bot;
|
||||
/// use telebofr::types::Message;
|
||||
/// async fn run() {
|
||||
/// use std::convert::Infallible;
|
||||
/// use telebofr::{
|
||||
/// dispatching::{
|
||||
/// dispatchers::filter::{error_policy::ErrorPolicy, FilterDispatcher},
|
||||
/// updater::polling,
|
||||
/// },
|
||||
/// };
|
||||
///
|
||||
/// async fn handle_edited_message(mes: Message) {
|
||||
/// println!("Edited message: {:?}", mes)
|
||||
/// }
|
||||
///
|
||||
/// let bot = Bot::new("TOKEN");
|
||||
///
|
||||
/// // create dispatching which handlers can't fail
|
||||
/// // with error policy that just ignores all errors (that can't ever happen)
|
||||
/// let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
|
||||
/// // Add 'handler' that will handle all messages sent to the bot
|
||||
/// .message_handler(true, |mes: Message| {
|
||||
/// async move { println!("New message: {:?}", mes) }
|
||||
/// })
|
||||
/// // Add 'handler' that will handle all
|
||||
/// // messages edited in chat with the bot
|
||||
/// .edited_message_handler(true, handle_edited_message);
|
||||
///
|
||||
/// // Start dispatching updates from long polling
|
||||
/// dp.dispatch(polling(&bot)).await;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`std::fmt::Debug`]: std::fmt::Debug
|
||||
/// [Custom error policy]:
|
||||
/// crate::dispatching::filter::error_policy::ErrorPolicy::Custom [updater]:
|
||||
/// crate::dispatching::updater
|
||||
pub struct FilterDispatcher<'a, E> {
|
||||
message_handlers: Handlers<'a, Message, E>,
|
||||
edited_message_handlers: Handlers<'a, Message, E>,
|
||||
channel_post_handlers: Handlers<'a, Message, E>,
|
||||
edited_channel_post_handlers: Handlers<'a, Message, E>,
|
||||
inline_query_handlers: Handlers<'a, (), E>,
|
||||
chosen_inline_result_handlers: Handlers<'a, ChosenInlineResult, E>,
|
||||
callback_query_handlers: Handlers<'a, CallbackQuery, E>,
|
||||
error_policy: ErrorPolicy<'a, E>,
|
||||
}
|
||||
|
||||
impl<'a, E> FilterDispatcher<'a, E>
|
||||
where
|
||||
E: std::fmt::Debug, // TODO: Is this really necessary?
|
||||
{
|
||||
pub fn new(error_policy: ErrorPolicy<'a, E>) -> Self {
|
||||
FilterDispatcher {
|
||||
message_handlers: Vec::new(),
|
||||
edited_message_handlers: Vec::new(),
|
||||
channel_post_handlers: Vec::new(),
|
||||
edited_channel_post_handlers: Vec::new(),
|
||||
inline_query_handlers: Vec::new(),
|
||||
chosen_inline_result_handlers: Vec::new(),
|
||||
callback_query_handlers: Vec::new(),
|
||||
error_policy,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.message_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn edited_message_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.edited_message_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn channel_post_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.channel_post_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn edited_channel_post_handler<F, H>(
|
||||
mut self,
|
||||
filter: F,
|
||||
handler: H,
|
||||
) -> Self
|
||||
where
|
||||
F: Filter<Message> + 'a,
|
||||
H: Handler<'a, Message, E> + 'a,
|
||||
{
|
||||
self.edited_channel_post_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inline_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<()> + 'a,
|
||||
H: Handler<'a, (), E> + 'a,
|
||||
{
|
||||
self.inline_query_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn chosen_inline_result_handler<F, H>(
|
||||
mut self,
|
||||
filter: F,
|
||||
handler: H,
|
||||
) -> Self
|
||||
where
|
||||
F: Filter<ChosenInlineResult> + 'a,
|
||||
H: Handler<'a, ChosenInlineResult, E> + 'a,
|
||||
{
|
||||
self.chosen_inline_result_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn callback_query_handler<F, H>(mut self, filter: F, handler: H) -> Self
|
||||
where
|
||||
F: Filter<CallbackQuery> + 'a,
|
||||
H: Handler<'a, CallbackQuery, E> + 'a,
|
||||
{
|
||||
self.callback_query_handlers
|
||||
.push((Box::new(filter), Box::new(handler)));
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: Can someone simplify this?
|
||||
pub async fn dispatch<U>(&mut self, updates: U)
|
||||
where
|
||||
U: Updater + 'a,
|
||||
{
|
||||
updates
|
||||
.for_each(|res| {
|
||||
async {
|
||||
let res = res;
|
||||
let Update { kind, id } = match res {
|
||||
Ok(upd) => upd,
|
||||
_ => return, // TODO: proper error handling
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"Handled update#{id:?}: {kind:?}",
|
||||
id = id,
|
||||
kind = kind
|
||||
);
|
||||
|
||||
match kind {
|
||||
UpdateKind::Message(mes) => {
|
||||
self.handle(mes, &self.message_handlers).await
|
||||
}
|
||||
UpdateKind::EditedMessage(mes) => {
|
||||
self.handle(mes, &self.edited_message_handlers)
|
||||
.await;
|
||||
}
|
||||
UpdateKind::ChannelPost(post) => {
|
||||
self.handle(post, &self.channel_post_handlers)
|
||||
.await;
|
||||
}
|
||||
UpdateKind::EditedChannelPost(post) => {
|
||||
self.handle(
|
||||
post,
|
||||
&self.edited_channel_post_handlers,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
UpdateKind::InlineQuery(query) => {
|
||||
self.handle(query, &self.inline_query_handlers)
|
||||
.await;
|
||||
}
|
||||
UpdateKind::ChosenInlineResult(result) => {
|
||||
self.handle(
|
||||
result,
|
||||
&self.chosen_inline_result_handlers,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
UpdateKind::CallbackQuery(callback) => {
|
||||
self.handle(
|
||||
callback,
|
||||
&self.callback_query_handlers,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn handle<T>(&self, update: T, handlers: &Handlers<'a, T, E>)
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
{
|
||||
let handler = handlers.iter().find_map(|e| {
|
||||
let (filter, handler) = e;
|
||||
if filter.test(&update) {
|
||||
Some(handler)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
match handler {
|
||||
Some(handler) => {
|
||||
if let Err(err) = handler.handle(update).await {
|
||||
self.error_policy.handle_error(err).await
|
||||
}
|
||||
}
|
||||
None => {
|
||||
log::warn!("unhandled update {:?}", update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(? Send)]
|
||||
impl<'a, U, E> Dispatcher<'a, U> for FilterDispatcher<'a, E>
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
U: Updater + 'a,
|
||||
{
|
||||
async fn dispatch(&'a mut self, updater: U) {
|
||||
FilterDispatcher::dispatch(self, updater).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use futures::Stream;
|
||||
|
||||
use crate::{
|
||||
dispatching::{
|
||||
dispatchers::filter::{
|
||||
error_policy::ErrorPolicy, FilterDispatcher,
|
||||
},
|
||||
updater::StreamUpdater,
|
||||
},
|
||||
types::{
|
||||
Chat, ChatKind, ForwardKind, MediaKind, Message, MessageKind,
|
||||
Sender, Update, UpdateKind, User,
|
||||
},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn first_handler_executes_1_time() {
|
||||
let counter = &AtomicI32::new(0);
|
||||
let counter2 = &AtomicI32::new(0);
|
||||
|
||||
let mut dp = FilterDispatcher::<Infallible>::new(ErrorPolicy::Ignore)
|
||||
.message_handler(true, |_mes: Message| {
|
||||
async move {
|
||||
counter.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
})
|
||||
.message_handler(true, |_mes: Message| {
|
||||
async move {
|
||||
counter2.fetch_add(1, Ordering::SeqCst);
|
||||
Ok::<_, Infallible>(())
|
||||
}
|
||||
});
|
||||
|
||||
dp.dispatch(one_message_updater()).await;
|
||||
|
||||
assert_eq!(counter.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(counter2.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
|
||||
fn message() -> Message {
|
||||
Message {
|
||||
id: 6534,
|
||||
date: 1567898953,
|
||||
chat: Chat {
|
||||
id: 218485655,
|
||||
photo: None,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
first_name: Some("W".to_string()),
|
||||
last_name: None,
|
||||
username: Some("WaffleLapkin".to_string()),
|
||||
},
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 457569668,
|
||||
is_bot: true,
|
||||
first_name: "BT".to_string(),
|
||||
last_name: None,
|
||||
username: Some("BloodyTestBot".to_string()),
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Text {
|
||||
text: "text".to_string(),
|
||||
entities: vec![],
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn message_update() -> Update {
|
||||
Update {
|
||||
id: 0,
|
||||
kind: UpdateKind::Message(message()),
|
||||
}
|
||||
}
|
||||
|
||||
fn one_message_updater(
|
||||
) -> StreamUpdater<impl Stream<Item = Result<Update, Infallible>>> {
|
||||
use futures::{future::ready, stream};
|
||||
|
||||
StreamUpdater::new(stream::once(ready(Ok(message_update()))))
|
||||
}
|
||||
}
|
3
src/dispatching/dispatchers/mod.rs
Normal file
3
src/dispatching/dispatchers/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub use filter::FilterDispatcher;
|
||||
|
||||
pub mod filter;
|
110
src/dispatching/filters/command.rs
Normal file
110
src/dispatching/filters/command.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use crate::{dispatching::Filter, types::Message};
|
||||
|
||||
pub struct CommandFilter {
|
||||
command: String,
|
||||
}
|
||||
|
||||
impl Filter<Message> for CommandFilter {
|
||||
fn test(&self, value: &Message) -> bool {
|
||||
match value.text() {
|
||||
Some(text) => match text.split_whitespace().next() {
|
||||
Some(command) => self.command == command,
|
||||
None => false,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandFilter {
|
||||
pub fn new<T>(command: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self {
|
||||
command: '/'.to_string() + &command.into(),
|
||||
}
|
||||
}
|
||||
pub fn with_prefix<T>(command: T, prefix: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self {
|
||||
command: prefix.into() + &command.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::{
|
||||
Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn commands_are_equal() {
|
||||
let filter = CommandFilter::new("command".to_string());
|
||||
let message = create_message_with_text("/command".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commands_are_not_equal() {
|
||||
let filter = CommandFilter::new("command".to_string());
|
||||
let message =
|
||||
create_message_with_text("/not_equal_command".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_have_args() {
|
||||
let filter = CommandFilter::new("command".to_string());
|
||||
let message =
|
||||
create_message_with_text("/command arg1 arg2".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_have_only_whitespace() {
|
||||
let filter = CommandFilter::new("command".to_string());
|
||||
let message = create_message_with_text(" ".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
fn create_message_with_text(text: String) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
date: 0,
|
||||
chat: Chat {
|
||||
id: 0,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
username: None,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
},
|
||||
photo: None,
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 0,
|
||||
is_bot: false,
|
||||
first_name: "".to_string(),
|
||||
last_name: None,
|
||||
username: None,
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Text {
|
||||
text,
|
||||
entities: vec![],
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ pub trait Filter<T> {
|
|||
}
|
||||
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::Filter;
|
||||
/// use telebofr::dispatching::filters::Filter;
|
||||
///
|
||||
/// let closure = |i: &i32| -> bool { *i >= 42 };
|
||||
/// assert!(closure.test(&42));
|
||||
|
@ -22,13 +22,15 @@ impl<T, F: Fn(&T) -> bool> Filter<T> for F {
|
|||
}
|
||||
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::Filter;
|
||||
/// use telebofr::dispatching::filters::Filter;
|
||||
///
|
||||
/// assert!(true.test(&()));
|
||||
/// assert_eq!(false.test(&()), false);
|
||||
/// ```
|
||||
impl<T> Filter<T> for bool {
|
||||
fn test(&self, _: &T) -> bool { *self }
|
||||
fn test(&self, _: &T) -> bool {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
/// And filter.
|
||||
|
@ -40,7 +42,7 @@ impl<T> Filter<T> for bool {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{And, Filter};
|
||||
/// use telebofr::dispatching::filters::{And, Filter};
|
||||
///
|
||||
/// // Note: bool can be treated as `Filter` that always return self.
|
||||
/// assert_eq!(And::new(true, false).test(&()), false);
|
||||
|
@ -71,18 +73,17 @@ where
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{and, Filter};
|
||||
/// use telebofr::dispatching::filters::{and, Filter};
|
||||
///
|
||||
/// assert!(and(true, true).test(&()));
|
||||
/// assert_eq!(and(true, false).test(&()), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`And::new`]: crate::dispatcher::filter::And::new
|
||||
/// [`And::new`]: crate::dispatching::filter::And::new
|
||||
pub fn and<A, B>(a: A, b: B) -> And<A, B> {
|
||||
And::new(a, b)
|
||||
}
|
||||
|
||||
|
||||
/// Or filter.
|
||||
///
|
||||
/// Passes if at least one underlying filters passes.
|
||||
|
@ -92,7 +93,7 @@ pub fn and<A, B>(a: A, b: B) -> And<A, B> {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Or, Filter};
|
||||
/// use telebofr::dispatching::filters::{Filter, Or};
|
||||
///
|
||||
/// // Note: bool can be treated as `Filter` that always return self.
|
||||
/// assert!(Or::new(true, false).test(&()));
|
||||
|
@ -123,25 +124,24 @@ where
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{or, Filter};
|
||||
/// use telebofr::dispatching::filters::{or, Filter};
|
||||
///
|
||||
/// assert!(or(true, false).test(&()));
|
||||
/// assert_eq!(or(false, false).test(&()), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`Or::new`]: crate::dispatcher::filter::Or::new
|
||||
/// [`Or::new`]: crate::dispatching::filter::Or::new
|
||||
pub fn or<A, B>(a: A, b: B) -> Or<A, B> {
|
||||
Or::new(a, b)
|
||||
}
|
||||
|
||||
|
||||
/// Not filter.
|
||||
///
|
||||
/// Passes if underlying filter don't pass.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Not, Filter};
|
||||
/// use telebofr::dispatching::filters::{Filter, Not};
|
||||
///
|
||||
/// // Note: bool can be treated as `Filter` that always return self.
|
||||
/// assert!(Not::new(false).test(&()));
|
||||
|
@ -169,13 +169,13 @@ where
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{not, Filter};
|
||||
/// use telebofr::dispatching::filters::{not, Filter};
|
||||
///
|
||||
/// assert!(not(false).test(&()));
|
||||
/// assert_eq!(not(true).test(&()), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`Not::new`]: crate::dispatcher::filter::Not::new
|
||||
/// [`Not::new`]: crate::dispatching::filter::Not::new
|
||||
pub fn not<A>(a: A) -> Not<A> {
|
||||
Not::new(a)
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ pub fn not<A>(a: A) -> Not<A> {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::{all, dispatcher::filter::Filter};
|
||||
/// use telebofr::{all, dispatching::filters::Filter};
|
||||
///
|
||||
/// assert!(all![true].test(&()));
|
||||
/// assert!(all![true, true].test(&()));
|
||||
|
@ -199,12 +199,12 @@ pub fn not<A>(a: A) -> Not<A> {
|
|||
/// assert_eq!(all![false, false].test(&()), false);
|
||||
/// ```
|
||||
///
|
||||
/// [filter]: crate::dispatcher::filter::Filter
|
||||
/// [filter]: crate::dispatching::filter::Filter
|
||||
#[macro_export]
|
||||
macro_rules! all {
|
||||
($one:expr) => { $one };
|
||||
($head:expr, $($tail:tt)+) => {
|
||||
$crate::dispatcher::filter::And::new(
|
||||
$crate::dispatching::filters::And::new(
|
||||
$head,
|
||||
$crate::all!($($tail)+)
|
||||
)
|
||||
|
@ -218,7 +218,7 @@ macro_rules! all {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::{any, dispatcher::filter::Filter};
|
||||
/// use telebofr::{any, dispatching::filters::Filter};
|
||||
///
|
||||
/// assert!(any![true].test(&()));
|
||||
/// assert!(any![true, true].test(&()));
|
||||
|
@ -230,24 +230,23 @@ macro_rules! all {
|
|||
/// assert_eq!(any![false, false, false].test(&()), false);
|
||||
/// ```
|
||||
///
|
||||
/// [filter]: crate::dispatcher::filter::Filter
|
||||
/// [filter]: crate::dispatching::filter::Filter
|
||||
#[macro_export]
|
||||
macro_rules! any {
|
||||
($one:expr) => { $one };
|
||||
($head:expr, $($tail:tt)+) => {
|
||||
$crate::dispatcher::filter::Or::new(
|
||||
$crate::dispatching::filters::Or::new(
|
||||
$head,
|
||||
$crate::all!($($tail)+)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Simple wrapper around `Filter` that adds `|` and `&` operators.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Filter, f, F, And, Or};
|
||||
/// use telebofr::dispatching::filters::{f, And, Filter, Or, F};
|
||||
///
|
||||
/// let flt1 = |i: &i32| -> bool { *i > 17 };
|
||||
/// let flt2 = |i: &i32| -> bool { *i < 42 };
|
||||
|
@ -259,7 +258,6 @@ macro_rules! any {
|
|||
/// assert_eq!(and.test(&50), false); // `flt2` doesn't pass
|
||||
/// assert_eq!(and.test(&16), false); // `flt1` doesn't pass
|
||||
///
|
||||
///
|
||||
/// let or = f(flt1) | flt3;
|
||||
/// assert!(or.test(&19)); // `flt1` passes
|
||||
/// assert!(or.test(&16)); // `flt2` passes
|
||||
|
@ -267,9 +265,8 @@ macro_rules! any {
|
|||
///
|
||||
/// assert_eq!(or.test(&17), false); // both don't pass
|
||||
///
|
||||
///
|
||||
/// // Note: only first filter in chain should be wrapped in `f(...)`
|
||||
/// let complicated: F<Or<And<_, _>, _>>= f(flt1) & flt2 | flt3;
|
||||
/// let complicated: F<Or<And<_, _>, _>> = f(flt1) & flt2 | flt3;
|
||||
/// assert!(complicated.test(&2)); // `flt3` passes
|
||||
/// assert!(complicated.test(&21)); // `flt1` and `flt2` pass
|
||||
///
|
||||
|
@ -280,14 +277,14 @@ pub struct F<A>(A);
|
|||
|
||||
/// Constructor fn for [F]
|
||||
///
|
||||
/// [F]: crate::dispatcher::filter::F;
|
||||
/// [F]: crate::dispatching::filter::F;
|
||||
pub fn f<A>(a: A) -> F<A> {
|
||||
F(a)
|
||||
}
|
||||
|
||||
impl<T, A> Filter<T> for F<A>
|
||||
where
|
||||
A: Filter<T>
|
||||
A: Filter<T>,
|
||||
{
|
||||
fn test(&self, value: &T) -> bool {
|
||||
self.0.test(value)
|
||||
|
@ -310,13 +307,14 @@ impl<A, B> std::ops::BitOr<B> for F<A> {
|
|||
}
|
||||
}
|
||||
|
||||
/* workaround for `E0207` compiler error */
|
||||
/// Extensions for filters
|
||||
pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
||||
pub trait FilterExt<T> {
|
||||
/// Alias for [`Not::new`]
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Filter, FilterExt};
|
||||
/// use telebofr::dispatching::filters::{Filter, FilterExt};
|
||||
///
|
||||
/// let flt = |i: &i32| -> bool { *i > 0 };
|
||||
/// let flt = flt.not();
|
||||
|
@ -324,8 +322,11 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
|||
/// assert_eq!(flt.test(&1), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`Not::new`]: crate::dispatcher::filter::Not::new
|
||||
fn not(self) -> Not<Self> where Self: Sized {
|
||||
/// [`Not::new`]: crate::dispatching::filter::Not::new
|
||||
fn not(self) -> Not<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Not::new(self)
|
||||
}
|
||||
|
||||
|
@ -333,7 +334,7 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Filter, FilterExt};
|
||||
/// use telebofr::dispatching::filters::{Filter, FilterExt};
|
||||
///
|
||||
/// let flt = |i: &i32| -> bool { *i > 0 };
|
||||
/// let flt = flt.and(|i: &i32| *i < 42);
|
||||
|
@ -343,8 +344,11 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
|||
/// assert_eq!(flt.test(&43), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`Not::new`]: crate::dispatcher::filter::And::new
|
||||
fn and<B>(self, other: B) -> And<Self, B> where Self: Sized {
|
||||
/// [`Not::new`]: crate::dispatching::filter::And::new
|
||||
fn and<B>(self, other: B) -> And<Self, B>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
And::new(self, other)
|
||||
}
|
||||
|
||||
|
@ -352,7 +356,7 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
|||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// use async_telegram_bot::dispatcher::filter::{Filter, FilterExt};
|
||||
/// use telebofr::dispatching::filters::{Filter, FilterExt};
|
||||
///
|
||||
/// let flt = |i: &i32| -> bool { *i < 0 };
|
||||
/// let flt = flt.or(|i: &i32| *i > 42);
|
||||
|
@ -362,8 +366,11 @@ pub trait FilterExt<T /* workaround for `E0207` compiler error */> {
|
|||
/// assert_eq!(flt.test(&17), false);
|
||||
/// ```
|
||||
///
|
||||
/// [`Not::new`]: crate::dispatcher::filter::Or::new
|
||||
fn or<B>(self, other: B) -> Or<Self, B> where Self: Sized {
|
||||
/// [`Not::new`]: crate::dispatching::filter::Or::new
|
||||
fn or<B>(self, other: B) -> Or<Self, B>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Or::new(self, other)
|
||||
}
|
||||
}
|
100
src/dispatching/filters/message_caption.rs
Normal file
100
src/dispatching/filters/message_caption.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use crate::{dispatching::Filter, types::Message};
|
||||
|
||||
/// Filter which compare caption of media with another text.
|
||||
/// Returns true if the caption of media is equal to another text, otherwise
|
||||
/// false.
|
||||
///
|
||||
/// NOTE: filter compares only caption of media, does not compare text of
|
||||
/// message!
|
||||
///
|
||||
/// If you want to compare text of message use
|
||||
/// [MessageTextFilter]
|
||||
///
|
||||
/// If you want to compare text and caption use
|
||||
/// [MessageTextCaptionFilter]
|
||||
///
|
||||
/// [MessageTextFilter]: telebofr::dispatching::filters::MessageTextFilter
|
||||
/// [MessageTextCaptionFilter]:
|
||||
/// telebofr::dispatching::filters::MessageTextCaptionFilter
|
||||
pub struct MessageCaptionFilter {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl Filter<Message> for MessageCaptionFilter {
|
||||
fn test(&self, value: &Message) -> bool {
|
||||
match value.caption() {
|
||||
Some(caption) => self.text == caption,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageCaptionFilter {
|
||||
pub fn new<T>(text: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::{
|
||||
Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn captions_are_equal() {
|
||||
let filter = MessageCaptionFilter::new("caption".to_string());
|
||||
let message = create_message_with_caption("caption".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captions_are_not_equal() {
|
||||
let filter = MessageCaptionFilter::new("caption".to_string());
|
||||
let message =
|
||||
create_message_with_caption("not equal caption".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
fn create_message_with_caption(caption: String) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
date: 0,
|
||||
chat: Chat {
|
||||
id: 0,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
username: None,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
},
|
||||
photo: None,
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 0,
|
||||
is_bot: false,
|
||||
first_name: "".to_string(),
|
||||
last_name: None,
|
||||
username: None,
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Photo {
|
||||
photo: vec![],
|
||||
caption: Some(caption),
|
||||
caption_entities: vec![],
|
||||
media_group_id: None,
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
95
src/dispatching/filters/message_text.rs
Normal file
95
src/dispatching/filters/message_text.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use crate::{dispatching::Filter, types::Message};
|
||||
|
||||
/// Filter which compare message text with another text.
|
||||
/// Returns true if the message text is equal to another text, otherwise false.
|
||||
///
|
||||
/// NOTE: filter compares only text message, does not compare caption of media!
|
||||
///
|
||||
/// If you want to compare caption use
|
||||
/// [MessageCaptionFilter]
|
||||
///
|
||||
/// If you want to compare text and caption use
|
||||
/// [MessageTextCaptionFilter]
|
||||
///
|
||||
/// [MessageCaptionFilter]: telebofr::dispatching::filters::MessageCaptionFilter
|
||||
/// [MessageTextCaptionFilter]:
|
||||
/// telebofr::dispatching::filters::MessageTextCaptionFilter
|
||||
pub struct MessageTextFilter {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl Filter<Message> for MessageTextFilter {
|
||||
fn test(&self, value: &Message) -> bool {
|
||||
match value.text() {
|
||||
Some(text) => self.text == text,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageTextFilter {
|
||||
pub fn new<T>(text: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::{
|
||||
Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn texts_are_equal() {
|
||||
let filter = MessageTextFilter::new("text");
|
||||
let message = create_message_with_text("text".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn texts_are_not_equal() {
|
||||
let filter = MessageTextFilter::new("text");
|
||||
let message = create_message_with_text("not equal text".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
fn create_message_with_text(text: String) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
date: 0,
|
||||
chat: Chat {
|
||||
id: 0,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
username: None,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
},
|
||||
photo: None,
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 0,
|
||||
is_bot: false,
|
||||
first_name: "".to_string(),
|
||||
last_name: None,
|
||||
username: None,
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Text {
|
||||
text,
|
||||
entities: vec![],
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
152
src/dispatching/filters/message_text_caption.rs
Normal file
152
src/dispatching/filters/message_text_caption.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use crate::{dispatching::Filter, types::Message};
|
||||
|
||||
/// Filter which compare message text or caption of media with another text.
|
||||
/// Returns true if the message text or caption of media is equal to another
|
||||
/// text, otherwise false.
|
||||
///
|
||||
/// NOTE: filter compares text of message or if it is not exists, compares
|
||||
/// caption of the message!
|
||||
///
|
||||
/// If you want to compare only caption use
|
||||
/// [MessageCaptionFilter]
|
||||
///
|
||||
/// If you want to compare only text use
|
||||
/// [MessageTextFilter]
|
||||
///
|
||||
/// [MessageCaptionFilter]: telebofr::dispatching::filters::MessageCaptionFilter
|
||||
/// [MessageTextFilter]: telebofr::filter::filters::MessageTextFilter
|
||||
pub struct MessageTextCaptionFilter {
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl Filter<Message> for MessageTextCaptionFilter {
|
||||
fn test(&self, value: &Message) -> bool {
|
||||
match value.text() {
|
||||
Some(text) => self.text == text,
|
||||
None => match value.caption() {
|
||||
Some(caption) => self.text == caption,
|
||||
None => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageTextCaptionFilter {
|
||||
pub fn new<T>(text: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self { text: text.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::{
|
||||
Chat, ChatKind, ForwardKind, MediaKind, MessageKind, Sender, User,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn texts_are_equal() {
|
||||
let filter = MessageTextCaptionFilter::new("text");
|
||||
let message = create_message_with_text("text".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn texts_are_not_equal() {
|
||||
let filter = MessageTextCaptionFilter::new("text");
|
||||
let message = create_message_with_text("not equal text".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
fn create_message_with_text(text: String) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
date: 0,
|
||||
chat: Chat {
|
||||
id: 0,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
username: None,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
},
|
||||
photo: None,
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 0,
|
||||
is_bot: false,
|
||||
first_name: "".to_string(),
|
||||
last_name: None,
|
||||
username: None,
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Text {
|
||||
text,
|
||||
entities: vec![],
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captions_are_equal() {
|
||||
let filter = MessageTextCaptionFilter::new("caption".to_string());
|
||||
let message = create_message_with_caption("caption".to_string());
|
||||
assert!(filter.test(&message));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captions_are_not_equal() {
|
||||
let filter = MessageTextCaptionFilter::new("caption".to_string());
|
||||
let message =
|
||||
create_message_with_caption("not equal caption".to_string());
|
||||
assert_eq!(filter.test(&message), false);
|
||||
}
|
||||
|
||||
fn create_message_with_caption(caption: String) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
date: 0,
|
||||
chat: Chat {
|
||||
id: 0,
|
||||
kind: ChatKind::Private {
|
||||
type_: (),
|
||||
username: None,
|
||||
first_name: None,
|
||||
last_name: None,
|
||||
},
|
||||
photo: None,
|
||||
},
|
||||
kind: MessageKind::Common {
|
||||
from: Sender::User(User {
|
||||
id: 0,
|
||||
is_bot: false,
|
||||
first_name: "".to_string(),
|
||||
last_name: None,
|
||||
username: None,
|
||||
language_code: None,
|
||||
}),
|
||||
forward_kind: ForwardKind::Origin {
|
||||
reply_to_message: None,
|
||||
},
|
||||
edit_date: None,
|
||||
media_kind: MediaKind::Photo {
|
||||
photo: vec![],
|
||||
caption: Some(caption),
|
||||
caption_entities: vec![],
|
||||
media_group_id: None,
|
||||
},
|
||||
reply_markup: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
13
src/dispatching/filters/mod.rs
Normal file
13
src/dispatching/filters/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
pub use main::*;
|
||||
|
||||
pub use command::*;
|
||||
pub use message_caption::*;
|
||||
pub use message_text::*;
|
||||
pub use message_text_caption::*;
|
||||
|
||||
mod main;
|
||||
|
||||
mod command;
|
||||
mod message_caption;
|
||||
mod message_text;
|
||||
mod message_text_caption;
|
|
@ -1,12 +1,15 @@
|
|||
use std::{future::Future, pin::Pin};
|
||||
|
||||
use futures::FutureExt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub type HandlerResult<E> = Result<(), E>;
|
||||
|
||||
/// Asynchronous handler for event `T` (like `&self, I -> Future` fn)
|
||||
pub trait Handler<'a, T, E> {
|
||||
fn handle(&self, value: T) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>>;
|
||||
fn handle(
|
||||
&self,
|
||||
value: T,
|
||||
) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>>;
|
||||
}
|
||||
|
||||
pub trait IntoHandlerResult<E> {
|
||||
|
@ -32,7 +35,10 @@ where
|
|||
R: IntoHandlerResult<E> + 'a,
|
||||
E: 'a,
|
||||
{
|
||||
fn handle(&self, value: T) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>> {
|
||||
fn handle(
|
||||
&self,
|
||||
value: T,
|
||||
) -> Pin<Box<dyn Future<Output = HandlerResult<E>> + 'a>> {
|
||||
Box::pin(self(value).map(IntoHandlerResult::into_hr))
|
||||
}
|
||||
}
|
15
src/dispatching/mod.rs
Normal file
15
src/dispatching/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
//! Update dispatching.
|
||||
|
||||
use async_trait::async_trait;
|
||||
pub use filters::Filter;
|
||||
pub use handler::Handler;
|
||||
|
||||
pub mod dispatchers;
|
||||
pub mod filters;
|
||||
pub mod handler;
|
||||
pub mod updater;
|
||||
|
||||
#[async_trait(? Send)]
|
||||
pub trait Dispatcher<'a, U> {
|
||||
async fn dispatch(&'a mut self, updater: U);
|
||||
}
|
|
@ -3,19 +3,17 @@ use std::{
|
|||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use pin_project::pin_project;
|
||||
use futures::{Stream, StreamExt, stream};
|
||||
use futures::{stream, Stream, StreamExt};
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
types::Update,
|
||||
RequestError,
|
||||
};
|
||||
use pin_project::pin_project;
|
||||
|
||||
use crate::{bot::Bot, types::Update, RequestError};
|
||||
|
||||
// Currently just a placeholder, but I'll add here some methods
|
||||
/// Updater is stream of updates.
|
||||
///
|
||||
/// Telegram supports 2 ways of [getting updates]: [long polling](Long Polling) and webhook
|
||||
/// Telegram supports 2 ways of [getting updates]: [long polling](Long Polling)
|
||||
/// and webhook
|
||||
///
|
||||
/// ## Long Polling
|
||||
///
|
||||
|
@ -99,12 +97,16 @@ use crate::{
|
|||
/// [GetUpdates]: crate::requests::GetUpdates
|
||||
/// [getting updates]: https://core.telegram.org/bots/api#getting-updates
|
||||
/// [wiki]: https://en.wikipedia.org/wiki/Push_technology#Long_polling
|
||||
pub trait Updater<E>: Stream<Item=Result<Update, E>> {}
|
||||
pub trait Updater:
|
||||
Stream<Item = Result<Update, <Self as Updater>::Error>>
|
||||
{
|
||||
type Error;
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct StreamUpdater<S> {
|
||||
#[pin]
|
||||
stream: S
|
||||
stream: S,
|
||||
}
|
||||
|
||||
impl<S> StreamUpdater<S> {
|
||||
|
@ -113,35 +115,49 @@ impl<S> StreamUpdater<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, E> Stream for StreamUpdater<S> where S: Stream<Item=Result<Update, E>> {
|
||||
impl<S, E> Stream for StreamUpdater<S>
|
||||
where
|
||||
S: Stream<Item = Result<Update, E>>,
|
||||
{
|
||||
type Item = Result<Update, E>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
fn poll_next(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Self::Item>> {
|
||||
self.project().stream.poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, E> Updater<E> for StreamUpdater<S> where S: Stream<Item=Result<Update, E>> {}
|
||||
impl<S, E> Updater for StreamUpdater<S>
|
||||
where
|
||||
S: Stream<Item = Result<Update, E>>,
|
||||
{
|
||||
type Error = E;
|
||||
}
|
||||
|
||||
pub fn polling<'a>(bot: &'a Bot) -> impl Updater<RequestError> + 'a {
|
||||
let stream = stream::unfold((bot, 0), |(bot, mut offset)| async move {
|
||||
// this match converts Result<Vec<_>, _> -> Vec<Result<_, _>>
|
||||
let updates = match bot.get_updates().offset(offset).send().await {
|
||||
Ok(updates) => {
|
||||
if let Some(upd) = updates.last() {
|
||||
offset = upd.id + 1;
|
||||
pub fn polling<'a>(bot: &'a Bot) -> impl Updater<Error = RequestError> + 'a {
|
||||
let stream = stream::unfold((bot, 0), |(bot, mut offset)| {
|
||||
async move {
|
||||
// this match converts Result<Vec<_>, _> -> Vec<Result<_, _>>
|
||||
let updates = match bot.get_updates().offset(offset).send().await {
|
||||
Ok(updates) => {
|
||||
if let Some(upd) = updates.last() {
|
||||
offset = upd.id + 1;
|
||||
}
|
||||
updates.into_iter().map(Ok).collect::<Vec<_>>()
|
||||
}
|
||||
updates.into_iter().map(|u| Ok(u)).collect::<Vec<_>>()
|
||||
},
|
||||
Err(err) => vec![Err(err)]
|
||||
};
|
||||
Some((stream::iter(updates), (bot, offset)))
|
||||
Err(err) => vec![Err(err)],
|
||||
};
|
||||
Some((stream::iter(updates), (bot, offset)))
|
||||
}
|
||||
})
|
||||
.flatten();
|
||||
.flatten();
|
||||
|
||||
StreamUpdater { stream }
|
||||
}
|
||||
|
||||
// TODO implement webhook (this actually require webserver and probably we
|
||||
// should add cargo feature that adds webhook)
|
||||
//pub fn webhook<'a>(bot: &'a Bot, cfg: WebhookConfig) -> Updater<impl Stream<Item=Result<Update, ???>> + 'a> {}
|
||||
//pub fn webhook<'a>(bot: &'a cfg: WebhookConfig) -> Updater<impl
|
||||
// Stream<Item=Result<Update, ???>> + 'a> {}
|
|
@ -5,12 +5,13 @@ extern crate serde;
|
|||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
|
||||
pub use bot::Bot;
|
||||
pub use errors::{DownloadError, RequestError};
|
||||
|
||||
mod errors;
|
||||
mod network;
|
||||
|
||||
pub mod bot;
|
||||
pub mod dispatcher;
|
||||
mod bot;
|
||||
pub mod dispatching;
|
||||
pub mod requests;
|
||||
pub mod types;
|
||||
|
|
|
@ -11,8 +11,8 @@ pub async fn request_multipart<T>(
|
|||
method_name: &str,
|
||||
params: Form,
|
||||
) -> ResponseResult<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
process_response(
|
||||
client
|
||||
|
@ -30,8 +30,8 @@ pub async fn request_simple<T>(
|
|||
token: &str,
|
||||
method_name: &str,
|
||||
) -> ResponseResult<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
process_response(
|
||||
client
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use reqwest::StatusCode;
|
||||
|
||||
use crate::{
|
||||
requests::ResponseResult, types::ResponseParameters, RequestError,
|
||||
requests::ResponseResult,
|
||||
types::{False, ResponseParameters, True},
|
||||
RequestError,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -10,14 +12,14 @@ pub enum TelegramResponse<R> {
|
|||
Ok {
|
||||
/// A dummy field. Used only for deserialization.
|
||||
#[allow(dead_code)]
|
||||
ok: bool, // TODO: True type
|
||||
ok: True,
|
||||
|
||||
result: R,
|
||||
},
|
||||
Err {
|
||||
/// A dummy field. Used only for deserialization.
|
||||
#[allow(dead_code)]
|
||||
ok: bool, // TODO: False type
|
||||
ok: False,
|
||||
|
||||
description: String,
|
||||
error_code: u16,
|
||||
|
|
125
src/requests/answer_callback_query.rs
Normal file
125
src/requests/answer_callback_query.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::True,
|
||||
};
|
||||
|
||||
/// Use this method to send answers to callback queries sent from inline
|
||||
/// keyboards. The answer will be displayed to the user as a notification at the
|
||||
/// top of the chat screen or as an alert. On success, True is returned.
|
||||
///
|
||||
/// Alternatively, the user can be redirected to the specified Game URL. For
|
||||
/// this option to work, you must first create a game for your bot via
|
||||
/// @Botfather and accept the terms. Otherwise, you may use links like
|
||||
/// t.me/your_bot?start=XXXX that open your bot with a parameter.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct AnswerCallbackQuery<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the query to be answered.
|
||||
callback_query_id: String,
|
||||
|
||||
/// Text of the notification. If not specified, nothing will be shown to
|
||||
/// the user, 0-200 characters
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
text: Option<String>,
|
||||
|
||||
/// If true, an alert will be shown by the client instead of a notification
|
||||
/// at the top of the chat screen. Defaults to false.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
show_alert: Option<bool>,
|
||||
|
||||
/// URL that will be opened by the user's client. If you have created a
|
||||
/// Game and accepted the conditions via @Botfather, specify the URL that
|
||||
/// opens your game – note that this will only work if the query comes from
|
||||
/// a callback_game button.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
url: Option<String>,
|
||||
|
||||
/// The maximum amount of time in seconds that the result of the callback
|
||||
/// query may be cached client-side. Telegram apps will support caching
|
||||
/// starting in version 3.14. Defaults to 0.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
cache_time: Option<i32>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for AnswerCallbackQuery<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl AnswerCallbackQuery<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"answerCallbackQuery",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AnswerCallbackQuery<'a> {
|
||||
pub(crate) fn new<S>(bot: &'a Bot, callback_query_id: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
callback_query_id: callback_query_id.into(),
|
||||
text: None,
|
||||
show_alert: None,
|
||||
url: None,
|
||||
cache_time: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn callback_query_id<S>(mut self, value: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.callback_query_id = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text<S>(mut self, value: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.text = Some(value.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn show_alert<B>(mut self, value: B) -> Self
|
||||
where
|
||||
B: Into<bool>,
|
||||
{
|
||||
self.show_alert = Some(value.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn url<S>(mut self, value: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.url = Some(value.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cache_time<I>(mut self, value: I) -> Self
|
||||
where
|
||||
I: Into<i32>,
|
||||
{
|
||||
self.cache_time = Some(value.into());
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::True,
|
||||
};
|
||||
|
||||
|
@ -16,7 +17,7 @@ use crate::{
|
|||
/// [`Update`]: crate::types::Update
|
||||
pub struct AnswerPreCheckoutQuery<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the query to be answered
|
||||
pub pre_checkout_query_id: String,
|
||||
|
@ -48,8 +49,8 @@ impl Request for AnswerPreCheckoutQuery<'_> {
|
|||
impl AnswerPreCheckoutQuery<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"answerPreCheckoutQuery",
|
||||
&self,
|
||||
)
|
||||
|
@ -59,7 +60,7 @@ impl AnswerPreCheckoutQuery<'_> {
|
|||
|
||||
impl<'a> AnswerPreCheckoutQuery<'a> {
|
||||
pub(crate) fn new<S, B>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
pre_checkout_query_id: S,
|
||||
ok: B,
|
||||
) -> Self
|
||||
|
@ -68,7 +69,7 @@ impl<'a> AnswerPreCheckoutQuery<'a> {
|
|||
B: Into<bool>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
pre_checkout_query_id: pre_checkout_query_id.into(),
|
||||
ok: ok.into(),
|
||||
error_message: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ShippingOption, True},
|
||||
};
|
||||
|
||||
|
@ -15,7 +16,7 @@ use crate::{
|
|||
/// [`Update`]: crate::types::Update
|
||||
pub struct AnswerShippingQuery<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the query to be answered
|
||||
pub shipping_query_id: String,
|
||||
|
@ -49,8 +50,8 @@ impl Request for AnswerShippingQuery<'_> {
|
|||
impl AnswerShippingQuery<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"answerShippingQuery",
|
||||
&self,
|
||||
)
|
||||
|
@ -59,17 +60,13 @@ impl AnswerShippingQuery<'_> {
|
|||
}
|
||||
|
||||
impl<'a> AnswerShippingQuery<'a> {
|
||||
pub(crate) fn new<S, B>(
|
||||
ctx: RequestContext<'a>,
|
||||
shipping_query_id: S,
|
||||
ok: B,
|
||||
) -> Self
|
||||
pub(crate) fn new<S, B>(bot: &'a Bot, shipping_query_id: S, ok: B) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
B: Into<bool>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
shipping_query_id: shipping_query_id.into(),
|
||||
ok: ok.into(),
|
||||
shipping_options: None,
|
||||
|
|
73
src/requests/delete_chat_photo.rs
Normal file
73
src/requests/delete_chat_photo.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DeleteChatPhoto<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for DeleteChatPhoto<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl DeleteChatPhoto<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"deleteChatPhoto",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DeleteChatPhoto<'a> {
|
||||
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let method = DeleteChatPhoto::new(&bot, chat_id);
|
||||
|
||||
let expected = r#"{"chat_id":123}"#;
|
||||
let actual = serde_json::to_string::<DeleteChatPhoto>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
64
src/requests/delete_chat_sticker_set.rs
Normal file
64
src/requests/delete_chat_sticker_set.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
/// Use this method to delete a group sticker set from a supergroup. The bot
|
||||
/// must be an administrator in the chat for this to work and must have the
|
||||
/// appropriate admin rights. Use the field can_set_sticker_set optionally
|
||||
/// returned in getChat requests to check if the bot can use this method.
|
||||
/// Returns True on success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct DeleteChatStickerSet<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target
|
||||
/// supergroup (in the format @supergroupusername)
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for DeleteChatStickerSet<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl DeleteChatStickerSet<'_> {
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"deleteChatStickerSet",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DeleteChatStickerSet<'a> {
|
||||
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -17,7 +18,7 @@ use crate::{
|
|||
/// [`Message`]: crate::types::Message
|
||||
pub struct EditMessageLiveLocation<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// Required if inline_message_id is not specified. Unique identifier for
|
||||
|
@ -53,8 +54,8 @@ impl Request for EditMessageLiveLocation<'_> {
|
|||
impl EditMessageLiveLocation<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"editMessageLiveLocation",
|
||||
&self,
|
||||
)
|
||||
|
@ -63,17 +64,13 @@ impl EditMessageLiveLocation<'_> {
|
|||
}
|
||||
|
||||
impl<'a> EditMessageLiveLocation<'a> {
|
||||
pub(crate) fn new<Lt, Lg>(
|
||||
ctx: RequestContext<'a>,
|
||||
latitude: Lt,
|
||||
longitude: Lg,
|
||||
) -> Self
|
||||
pub(crate) fn new<Lt, Lg>(bot: &'a Bot, latitude: Lt, longitude: Lg) -> Self
|
||||
where
|
||||
Lt: Into<f64>,
|
||||
Lg: Into<f64>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: None,
|
||||
message_id: None,
|
||||
inline_message_id: None,
|
||||
|
|
74
src/requests/export_chat_invite_link.rs
Normal file
74
src/requests/export_chat_invite_link.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::ChatId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ExportCharInviteLink<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for ExportCharInviteLink<'_> {
|
||||
type Output = String;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl ExportCharInviteLink<'_> {
|
||||
async fn send(self) -> ResponseResult<String> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"exportChatInviteLink",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExportCharInviteLink<'a> {
|
||||
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let method = ExportCharInviteLink::new(&bot, chat_id);
|
||||
|
||||
let expected = r#"{"chat_id":123}"#;
|
||||
let actual =
|
||||
serde_json::to_string::<ExportCharInviteLink>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
|
@ -79,7 +79,10 @@ macro_rules! impl_for_struct {
|
|||
|
||||
impl_for_struct!(bool, i32, i64);
|
||||
|
||||
impl<T> IntoFormValue for Option<T> where T: IntoFormValue {
|
||||
impl<T> IntoFormValue for Option<T>
|
||||
where
|
||||
T: IntoFormValue,
|
||||
{
|
||||
fn into_form_value(self) -> Option<FormValue> {
|
||||
self.and_then(IntoFormValue::into_form_value)
|
||||
}
|
||||
|
@ -87,8 +90,8 @@ impl<T> IntoFormValue for Option<T> where T: IntoFormValue {
|
|||
|
||||
impl IntoFormValue for &[InputMedia] {
|
||||
fn into_form_value(self) -> Option<FormValue> {
|
||||
let json = serde_json::to_string(self)
|
||||
.expect("serde_json::to_string failed");
|
||||
let json =
|
||||
serde_json::to_string(self).expect("serde_json::to_string failed");
|
||||
Some(FormValue::Str(json))
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +116,7 @@ impl IntoFormValue for ChatId {
|
|||
fn into_form_value(self) -> Option<FormValue> {
|
||||
let string = match self {
|
||||
ChatId::Id(id) => id.to_string(),
|
||||
ChatId::ChannelUsername(username) => username.clone(),
|
||||
ChatId::ChannelUsername(username) => username,
|
||||
};
|
||||
Some(FormValue::Str(string))
|
||||
}
|
||||
|
@ -121,7 +124,7 @@ impl IntoFormValue for ChatId {
|
|||
|
||||
impl IntoFormValue for String {
|
||||
fn into_form_value(self) -> Option<FormValue> {
|
||||
Some(FormValue::Str(self.to_owned()))
|
||||
Some(FormValue::Str(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message},
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
/// [`Message`] is returned.
|
||||
pub struct ForwardMessage<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
|
@ -40,8 +41,8 @@ impl Request for ForwardMessage<'_> {
|
|||
impl ForwardMessage<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
self.ctx.client,
|
||||
self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"forwardMessage",
|
||||
&self,
|
||||
)
|
||||
|
@ -51,7 +52,7 @@ impl ForwardMessage<'_> {
|
|||
|
||||
impl<'a> ForwardMessage<'a> {
|
||||
pub(crate) fn new<C, Fc, M>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
from_chat_id: Fc,
|
||||
message_id: M,
|
||||
|
@ -62,7 +63,7 @@ impl<'a> ForwardMessage<'a> {
|
|||
M: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
from_chat_id: from_chat_id.into(),
|
||||
message_id: message_id.into(),
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{Chat, ChatId},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetChat<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or username
|
||||
/// of the target supergroup or channel (in the format @channelusername)
|
||||
chat_id: ChatId,
|
||||
|
@ -31,8 +32,8 @@ impl Request for GetChat<'_> {
|
|||
impl GetChat<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Chat> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getChat",
|
||||
&self,
|
||||
)
|
||||
|
@ -41,11 +42,21 @@ impl GetChat<'_> {
|
|||
}
|
||||
|
||||
impl<'a> GetChat<'a> {
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
pub(crate) fn new<F>(bot: &'a Bot, chat_id: F) -> Self
|
||||
where
|
||||
F: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
64
src/requests/get_chat_administrators.rs
Normal file
64
src/requests/get_chat_administrators.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, ChatMember},
|
||||
};
|
||||
|
||||
/// Use this method to get a list of administrators in a chat. On success,
|
||||
/// returns an Array of ChatMember objects that contains information about all
|
||||
/// chat administrators except other bots. If the chat is a group or a
|
||||
/// supergroup and no administrators were appointed, only the creator will be
|
||||
/// returned
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetChatAdministrators<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target
|
||||
/// supergroup or channel (in the format @channelusername)
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for GetChatAdministrators<'_> {
|
||||
type Output = Vec<ChatMember>;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl GetChatAdministrators<'_> {
|
||||
async fn send(&self) -> ResponseResult<Vec<ChatMember>> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getChatAdministrators",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetChatAdministrators<'a> {
|
||||
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self
|
||||
}
|
||||
}
|
74
src/requests/get_chat_member.rs
Normal file
74
src/requests/get_chat_member.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, ChatMember},
|
||||
};
|
||||
|
||||
/// Use this method to get information about a member of a chat. Returns a
|
||||
/// ChatMember object on success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target
|
||||
/// supergroup or channel (in the format @channelusername)
|
||||
chat_id: ChatId,
|
||||
|
||||
/// Unique identifier of the target user
|
||||
user_id: i32,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for GetChatMember<'_> {
|
||||
type Output = ChatMember;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl GetChatMember<'_> {
|
||||
async fn send(&self) -> ResponseResult<ChatMember> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getChatMember",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetChatMember<'a> {
|
||||
pub(crate) fn new<C, I>(bot: &'a Bot, chat_id: C, user_id: I) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
I: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
user_id: user_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_id<I>(mut self, value: I) -> Self
|
||||
where
|
||||
I: Into<i32>,
|
||||
{
|
||||
self.user_id = value.into();
|
||||
self
|
||||
}
|
||||
}
|
61
src/requests/get_chat_members_count.rs
Normal file
61
src/requests/get_chat_members_count.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{Chat, ChatId},
|
||||
};
|
||||
|
||||
/// Use this method to get the number of members in a chat. Returns Int on
|
||||
/// success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetChatMembersCount<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username
|
||||
/// of the target supergroup or channel (in the format @channelusername)
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for GetChatMembersCount<'_> {
|
||||
type Output = Chat;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl GetChatMembersCount<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Chat> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getChatMembersCount",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetChatMembersCount<'a> {
|
||||
pub fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::File,
|
||||
};
|
||||
|
||||
|
@ -16,7 +17,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetFile<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// File identifier to get info about
|
||||
pub file_id: String,
|
||||
}
|
||||
|
@ -33,8 +34,8 @@ impl Request for GetFile<'_> {
|
|||
impl GetFile<'_> {
|
||||
pub async fn send(self) -> ResponseResult<File> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getFile",
|
||||
&self,
|
||||
)
|
||||
|
@ -43,12 +44,12 @@ impl GetFile<'_> {
|
|||
}
|
||||
|
||||
impl<'a> GetFile<'a> {
|
||||
pub(crate) fn new<F>(ctx: RequestContext<'a>, value: F) -> Self
|
||||
pub(crate) fn new<F>(bot: &'a Bot, value: F) -> Self
|
||||
where
|
||||
F: Into<String>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
file_id: value.into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::User,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A simple method for testing your bot's auth token. Requires no parameters.
|
||||
/// A filter method for testing your bot's auth token. Requires no parameters.
|
||||
/// Returns basic information about the bot in form of a [`User`] object.
|
||||
pub struct GetMe<'a> {
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -24,12 +25,13 @@ impl Request for GetMe<'_> {
|
|||
|
||||
impl GetMe<'_> {
|
||||
pub async fn send(self) -> ResponseResult<User> {
|
||||
network::request_simple(self.ctx.client, self.ctx.token, "getMe").await
|
||||
network::request_simple(self.bot.client(), self.bot.token(), "getMe")
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GetMe<'a> {
|
||||
pub(crate) fn new(ctx: RequestContext<'a>) -> Self {
|
||||
GetMe { ctx }
|
||||
pub(crate) fn new(bot: &'a Bot) -> Self {
|
||||
GetMe { bot }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::Update,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetUpdates<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
pub offset: Option<i32>,
|
||||
pub limit: Option<u8>,
|
||||
|
@ -41,8 +42,8 @@ impl Request for GetUpdates<'_> {
|
|||
impl GetUpdates<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Vec<Update>> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getUpdates",
|
||||
&self,
|
||||
)
|
||||
|
@ -51,9 +52,9 @@ impl GetUpdates<'_> {
|
|||
}
|
||||
|
||||
impl<'a> GetUpdates<'a> {
|
||||
pub(crate) fn new(ctx: RequestContext<'a>) -> Self {
|
||||
pub(crate) fn new(bot: &'a Bot) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
offset: None,
|
||||
limit: None,
|
||||
timeout: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::UserProfilePhotos,
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct GetUserProfilePhotos<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier of the target user
|
||||
pub user_id: i32,
|
||||
/// Sequential number of the first photo to be returned. By default, all
|
||||
|
@ -36,8 +37,8 @@ impl Request for GetUserProfilePhotos<'_> {
|
|||
impl GetUserProfilePhotos<'_> {
|
||||
async fn send(self) -> ResponseResult<UserProfilePhotos> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"getUserProfilePhotos",
|
||||
&self,
|
||||
)
|
||||
|
@ -46,12 +47,12 @@ impl GetUserProfilePhotos<'_> {
|
|||
}
|
||||
|
||||
impl<'a> GetUserProfilePhotos<'a> {
|
||||
pub fn new<U>(ctx: RequestContext<'a>, user_id: U) -> Self
|
||||
pub fn new<U>(bot: &'a Bot, user_id: U) -> Self
|
||||
where
|
||||
U: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
user_id: user_id.into(),
|
||||
offset: None,
|
||||
limit: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
|
@ -14,7 +15,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct KickChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target group or username of the target
|
||||
/// supergroup or channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -39,8 +40,8 @@ impl Request for KickChatMember<'_> {
|
|||
impl KickChatMember<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.ctx.client,
|
||||
self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"kickChatMember",
|
||||
&self,
|
||||
)
|
||||
|
@ -49,17 +50,13 @@ impl KickChatMember<'_> {
|
|||
}
|
||||
|
||||
impl<'a> KickChatMember<'a> {
|
||||
pub(crate) fn new<C, U>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
user_id: U,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
user_id: user_id.into(),
|
||||
until_date: None,
|
||||
|
|
59
src/requests/leave_chat.rs
Normal file
59
src/requests/leave_chat.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::ChatId,
|
||||
};
|
||||
|
||||
/// Use this method for your bot to leave a group, supergroup or channel.
|
||||
/// Returns True on success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct LeaveChat<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or username
|
||||
/// of the target supergroup or channel (in the format @channelusername)
|
||||
chat_id: ChatId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for LeaveChat<'_> {
|
||||
type Output = bool;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl LeaveChat<'_> {
|
||||
pub async fn send(self) -> ResponseResult<bool> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"leaveChat",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LeaveChat<'a> {
|
||||
pub(crate) fn new<F>(bot: &'a Bot, chat_id: F) -> Self
|
||||
where
|
||||
F: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
}
|
||||
}
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,45 +1,70 @@
|
|||
//! Raw API functions.
|
||||
//! API requests.
|
||||
|
||||
use reqwest::Client;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::RequestError;
|
||||
|
||||
pub use self::{
|
||||
answer_pre_checkout_query::AnswerPreCheckoutQuery,
|
||||
answer_shipping_query::AnswerShippingQuery,
|
||||
edit_message_live_location::EditMessageLiveLocation,
|
||||
forward_message::ForwardMessage, get_chat::GetChat, get_file::GetFile,
|
||||
get_me::GetMe, get_updates::GetUpdates,
|
||||
get_user_profile_photos::GetUserProfilePhotos,
|
||||
kick_chat_member::KickChatMember, pin_chat_message::PinChatMessage,
|
||||
promote_chat_member::PromoteChatMember,
|
||||
restrict_chat_member::RestrictChatMember, send_animation::SendAnimation,
|
||||
send_audio::SendAudio, send_chat_action::SendChatAction,
|
||||
send_contact::SendContact, send_document::SendDocument,
|
||||
send_location::SendLocation, send_media_group::SendMediaGroup,
|
||||
send_message::SendMessage, send_photo::SendPhoto, send_poll::SendPoll,
|
||||
send_venue::SendVenue, send_video::SendVideo,
|
||||
send_video_note::SendVideoNote, send_voice::SendVoice,
|
||||
stop_message_live_location::StopMessageLiveLocation,
|
||||
unban_chat_member::UnbanChatMember, unpin_chat_message::UnpinChatMessage,
|
||||
};
|
||||
pub use answer_callback_query::*;
|
||||
pub use answer_pre_checkout_query::*;
|
||||
pub use answer_shipping_query::*;
|
||||
pub use delete_chat_photo::*;
|
||||
pub use delete_chat_sticker_set::*;
|
||||
pub use edit_message_live_location::*;
|
||||
pub use export_chat_invite_link::*;
|
||||
pub use forward_message::*;
|
||||
pub use get_chat::*;
|
||||
pub use get_chat_administrators::*;
|
||||
pub use get_chat_member::*;
|
||||
pub use get_chat_members_count::*;
|
||||
pub use get_file::*;
|
||||
pub use get_me::*;
|
||||
pub use get_updates::*;
|
||||
pub use get_user_profile_photos::*;
|
||||
pub use kick_chat_member::*;
|
||||
pub use leave_chat::*;
|
||||
pub use pin_chat_message::*;
|
||||
pub use promote_chat_member::*;
|
||||
pub use restrict_chat_member::*;
|
||||
pub use send_animation::*;
|
||||
pub use send_audio::*;
|
||||
pub use send_chat_action::*;
|
||||
pub use send_contact::*;
|
||||
pub use send_document::*;
|
||||
pub use send_location::*;
|
||||
pub use send_media_group::*;
|
||||
pub use send_message::*;
|
||||
pub use send_photo::*;
|
||||
pub use send_poll::*;
|
||||
pub use send_venue::*;
|
||||
pub use send_video::*;
|
||||
pub use send_video_note::*;
|
||||
pub use send_voice::*;
|
||||
pub use set_chat_description::*;
|
||||
pub use set_chat_permissions::*;
|
||||
pub use set_chat_photo::*;
|
||||
pub use set_chat_sticker_set::*;
|
||||
pub use set_chat_title::*;
|
||||
pub use stop_message_live_location::*;
|
||||
pub use unban_chat_member::*;
|
||||
pub use unpin_chat_message::*;
|
||||
|
||||
mod form_builder;
|
||||
mod utils;
|
||||
|
||||
mod answer_callback_query;
|
||||
mod answer_pre_checkout_query;
|
||||
mod answer_shipping_query;
|
||||
mod delete_chat_photo;
|
||||
mod delete_chat_sticker_set;
|
||||
mod edit_message_live_location;
|
||||
mod export_chat_invite_link;
|
||||
mod forward_message;
|
||||
mod get_chat;
|
||||
mod get_chat_administrators;
|
||||
mod get_chat_member;
|
||||
mod get_chat_members_count;
|
||||
mod get_file;
|
||||
mod get_me;
|
||||
mod get_updates;
|
||||
mod get_user_profile_photos;
|
||||
mod kick_chat_member;
|
||||
mod leave_chat;
|
||||
mod pin_chat_message;
|
||||
mod promote_chat_member;
|
||||
mod restrict_chat_member;
|
||||
|
@ -57,12 +82,20 @@ mod send_venue;
|
|||
mod send_video;
|
||||
mod send_video_note;
|
||||
mod send_voice;
|
||||
mod set_chat_description;
|
||||
mod set_chat_permissions;
|
||||
mod set_chat_photo;
|
||||
mod set_chat_sticker_set;
|
||||
mod set_chat_title;
|
||||
mod stop_message_live_location;
|
||||
mod unban_chat_member;
|
||||
mod unpin_chat_message;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
/// A type that is returned from `Request::send_boxed`.
|
||||
pub type ResponseResult<T> = Result<T, RequestError>;
|
||||
pub type ResponseResult<T> = Result<T, crate::RequestError>;
|
||||
|
||||
/// A request that can be sent to Telegram.
|
||||
#[async_trait]
|
||||
|
@ -73,12 +106,3 @@ pub trait Request {
|
|||
/// Send this request.
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output>;
|
||||
}
|
||||
|
||||
/// A context used to send all the requests.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RequestContext<'a> {
|
||||
/// An HTTPS client.
|
||||
pub client: &'a Client,
|
||||
/// A token of your bot.
|
||||
pub token: &'a str,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PinChatMessage<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or username
|
||||
/// of the target supergroup or channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -22,17 +23,13 @@ pub struct PinChatMessage<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PinChatMessage<'a> {
|
||||
pub(crate) fn new<C, M>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
message_id: M,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, M>(bot: &'a Bot, chat_id: C, message_id: M) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
M: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
message_id: message_id.into(),
|
||||
disable_notification: None,
|
||||
|
@ -59,8 +56,8 @@ impl Request for PinChatMessage<'_> {
|
|||
impl PinChatMessage<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"pinChatMessage",
|
||||
&self,
|
||||
)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PromoteChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -62,8 +63,8 @@ impl Request for PromoteChatMember<'_> {
|
|||
impl PromoteChatMember<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"promoteChatMember",
|
||||
&self,
|
||||
)
|
||||
|
@ -72,17 +73,13 @@ impl PromoteChatMember<'_> {
|
|||
}
|
||||
|
||||
impl<'a> PromoteChatMember<'a> {
|
||||
pub(crate) fn new<C, U>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
user_id: U,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
user_id: user_id.into(),
|
||||
can_change_info: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, ChatPermissions, True},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RestrictChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target chat or username of the target
|
||||
/// supergroup (in the format @supergroupusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -40,8 +41,8 @@ impl Request for RestrictChatMember<'_> {
|
|||
impl RestrictChatMember<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"restrictChatMember",
|
||||
&self,
|
||||
)
|
||||
|
@ -51,7 +52,7 @@ impl RestrictChatMember<'_> {
|
|||
|
||||
impl<'a> RestrictChatMember<'a> {
|
||||
pub(crate) fn new<C, U, P>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
user_id: U,
|
||||
permissions: P,
|
||||
|
@ -62,7 +63,7 @@ impl<'a> RestrictChatMember<'a> {
|
|||
P: Into<ChatPermissions>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
user_id: user_id.into(),
|
||||
permissions: permissions.into(),
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::network;
|
||||
use crate::requests::{Request, RequestContext, ResponseResult};
|
||||
use crate::types::{ChatId, Message, ParseMode, ReplyMarkup};
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
///TODO: add to bot api
|
||||
///Use this method to send animation files (GIF or H.264/MPEG-4 AVC video
|
||||
/// without sound). On success, the sent Message is returned. Bots can currently
|
||||
/// send animation files of up to 50 MB in size, this limit may be changed in
|
||||
|
@ -12,7 +14,7 @@ use crate::types::{ChatId, Message, ParseMode, ReplyMarkup};
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendAnimation<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -20,8 +22,7 @@ pub struct SendAnimation<'a> {
|
|||
/// exists on the Telegram servers (recommended), pass an HTTP URL as a
|
||||
/// String for Telegram to get an animation from the Internet, or upload a
|
||||
/// new animation using multipart/form-data. More info on Sending Files »
|
||||
pub animation: String,
|
||||
// InputFile or String
|
||||
pub animation: InputFile,
|
||||
///Duration of sent animation in seconds
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<u64>,
|
||||
|
@ -40,8 +41,7 @@ pub struct SendAnimation<'a> {
|
|||
/// if the thumbnail was uploaded using multipart/form-data under
|
||||
/// <file_attach_name> »
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumb: Option<String>,
|
||||
// InputFile or String Optional
|
||||
pub thumb: Option<InputFile>,
|
||||
///Animation caption (may also be used when resending animation by
|
||||
/// file_id), 0-1024 characters
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -76,8 +76,8 @@ impl Request for SendAnimation<'_> {
|
|||
impl SendAnimation<'_> {
|
||||
async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendAnimation",
|
||||
&self,
|
||||
)
|
||||
|
@ -86,17 +86,13 @@ impl SendAnimation<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendAnimation<'a> {
|
||||
pub(crate) fn new<C, S>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
animation: S,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, S>(bot: &'a Bot, chat_id: C, animation: S) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
S: Into<String>,
|
||||
S: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
animation: animation.into(),
|
||||
duration: None,
|
||||
|
@ -143,7 +139,7 @@ impl<'a> SendAnimation<'a> {
|
|||
}
|
||||
pub fn thumb<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.thumb = Some(value.into());
|
||||
self
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{
|
||||
form_builder::FormBuilder, Request, RequestContext, ResponseResult,
|
||||
},
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -18,7 +17,7 @@ use crate::{
|
|||
/// [`Message`]: crate::types::Message
|
||||
/// [`SendVoice`]: crate::requests::SendVoice
|
||||
pub struct SendAudio<'a> {
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
|
@ -86,10 +85,9 @@ impl SendAudio<'_> {
|
|||
.add("audio", self.audio)
|
||||
.add("thumb", self.thumb);
|
||||
|
||||
|
||||
network::request_multipart(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendAudio",
|
||||
params.build(),
|
||||
)
|
||||
|
@ -98,17 +96,13 @@ impl SendAudio<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendAudio<'a> {
|
||||
pub(crate) fn new<C, A>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
audio: A,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, A>(bot: &'a Bot, chat_id: C, audio: A) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
A: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
audio: audio.into(),
|
||||
caption: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatAction, ChatId, True},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendChatAction<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or
|
||||
/// username of the target channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -37,8 +38,8 @@ impl Request for SendChatAction<'_> {
|
|||
impl SendChatAction<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendChatAction",
|
||||
&self,
|
||||
)
|
||||
|
@ -47,17 +48,13 @@ impl SendChatAction<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendChatAction<'a> {
|
||||
pub(crate) fn new<Cid, Ca>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: Cid,
|
||||
action: Ca,
|
||||
) -> Self
|
||||
pub(crate) fn new<Cid, Ca>(bot: &'a Bot, chat_id: Cid, action: Ca) -> Self
|
||||
where
|
||||
Cid: Into<ChatId>,
|
||||
Ca: Into<ChatAction>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
action: action.into(),
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendContact<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or
|
||||
/// username of the target channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -54,8 +55,8 @@ impl Request for SendContact<'_> {
|
|||
impl SendContact<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendContact",
|
||||
&self,
|
||||
)
|
||||
|
@ -65,7 +66,7 @@ impl SendContact<'_> {
|
|||
|
||||
impl<'a> SendContact<'a> {
|
||||
pub(crate) fn new<C, P, F>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
phone_number: P,
|
||||
first_name: F,
|
||||
|
@ -76,7 +77,7 @@ impl<'a> SendContact<'a> {
|
|||
F: Into<String>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
phone_number: phone_number.into(),
|
||||
first_name: first_name.into(),
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
types::{ChatId, Message, ParseMode, ReplyMarkup},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
// TODO: add method to bot/api
|
||||
|
||||
///Use this method to send general files. On success, the sent Message is
|
||||
/// returned. Bots can currently send files of any type of up to 50 MB in size,
|
||||
/// this limit may be changed in the future.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendDocument<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or username of the target
|
||||
/// channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -22,8 +21,7 @@ pub struct SendDocument<'a> {
|
|||
/// the Telegram servers (recommended), pass an HTTP URL as a String for
|
||||
/// Telegram to get a file from the Internet, or upload a new one using
|
||||
/// multipart/form-data.»
|
||||
pub document: String,
|
||||
//InputFile or String
|
||||
pub document: InputFile,
|
||||
/// Thumbnail of the file sent; can be ignored if thumbnail generation for
|
||||
/// the file is supported server-side. The thumbnail should be in JPEG
|
||||
/// format and less than 200 kB in size. A thumbnail‘s width and height
|
||||
|
@ -33,8 +31,7 @@ pub struct SendDocument<'a> {
|
|||
/// if the thumbnail was uploaded using multipart/form-data under
|
||||
/// <file_attach_name>. More info on Sending Files »
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumb: Option<String>,
|
||||
//InputFile or String
|
||||
pub thumb: Option<InputFile>,
|
||||
/// Document caption (may also be used when resending documents by
|
||||
/// file_id), 0-1024 characters
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -69,8 +66,8 @@ impl Request for SendDocument<'_> {
|
|||
impl SendDocument<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendDocument",
|
||||
&self,
|
||||
)
|
||||
|
@ -79,17 +76,13 @@ impl SendDocument<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendDocument<'a> {
|
||||
pub(crate) fn new<C, D>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
document: D,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, D>(bot: &'a Bot, chat_id: C, document: D) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
D: Into<String>,
|
||||
D: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
document: document.into(),
|
||||
thumb: None,
|
||||
|
@ -111,7 +104,7 @@ impl<'a> SendDocument<'a> {
|
|||
|
||||
pub fn document<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.document = value.into();
|
||||
self
|
||||
|
@ -119,7 +112,7 @@ impl<'a> SendDocument<'a> {
|
|||
|
||||
pub fn thumb<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.thumb = Some(value.into());
|
||||
self
|
||||
|
|
|
@ -3,8 +3,9 @@ use serde::Serialize;
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
/// is returned.
|
||||
pub struct SendLocation<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
|
@ -50,8 +51,8 @@ impl Request for SendLocation<'_> {
|
|||
impl SendLocation<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendLocation",
|
||||
&self,
|
||||
)
|
||||
|
@ -61,7 +62,7 @@ impl SendLocation<'_> {
|
|||
|
||||
impl<'a> SendLocation<'a> {
|
||||
pub(crate) fn new<Lt, Lg, C>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
latitude: Lt,
|
||||
longitude: Lg,
|
||||
|
@ -72,7 +73,7 @@ impl<'a> SendLocation<'a> {
|
|||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
latitude: latitude.into(),
|
||||
longitude: longitude.into(),
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network::request_multipart,
|
||||
requests::{
|
||||
form_builder::FormBuilder, Request, RequestContext, ResponseResult,
|
||||
},
|
||||
types::{ChatId, InputMedia, Message, InputFile},
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{ChatId, InputFile, InputMedia, Message},
|
||||
};
|
||||
|
||||
/// Use this method to send a group of photos or videos as an album.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SendMediaGroup<'a> {
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
pub chat_id: ChatId,
|
||||
pub media: Vec<InputMedia>,
|
||||
|
@ -37,17 +36,20 @@ impl SendMediaGroup<'_> {
|
|||
.add("disable_notification", self.disable_notification)
|
||||
.add("reply_to_message_id", self.reply_to_message_id);
|
||||
|
||||
let form = self.media.into_iter().filter_map(|e| InputFile::from(e).into())
|
||||
.fold(form, |acc, path: std::path::PathBuf|
|
||||
acc.add_file(
|
||||
&path.file_name().unwrap().to_string_lossy().into_owned(),
|
||||
path,
|
||||
)
|
||||
);
|
||||
let form = self
|
||||
.media
|
||||
.into_iter()
|
||||
.filter_map(|e| InputFile::from(e).into())
|
||||
.fold(form, |acc, path: std::path::PathBuf| {
|
||||
acc.add_file(
|
||||
&path.file_name().unwrap().to_string_lossy().into_owned(),
|
||||
path,
|
||||
)
|
||||
});
|
||||
|
||||
request_multipart(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendMediaGroup",
|
||||
form.build(),
|
||||
)
|
||||
|
@ -56,17 +58,13 @@ impl SendMediaGroup<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendMediaGroup<'a> {
|
||||
pub(crate) fn new<C, M>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
media: M,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, M>(bot: &'a Bot, chat_id: C, media: M) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
M: Into<Vec<InputMedia>>,
|
||||
{
|
||||
SendMediaGroup {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
media: media.into(),
|
||||
disable_notification: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
/// returned.
|
||||
pub struct SendMessage<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
|
@ -55,8 +56,8 @@ impl Request for SendMessage<'_> {
|
|||
impl SendMessage<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
self.ctx.client,
|
||||
self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendMessage",
|
||||
&self,
|
||||
)
|
||||
|
@ -65,17 +66,13 @@ impl SendMessage<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendMessage<'a> {
|
||||
pub(crate) fn new<C, S>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
text: S,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, S>(bot: &'a Bot, chat_id: C, text: S) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
S: Into<String>,
|
||||
{
|
||||
SendMessage {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
text: text.into(),
|
||||
parse_mode: None,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{
|
||||
form_builder::FormBuilder, Request, RequestContext, ResponseResult,
|
||||
},
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -12,7 +11,7 @@ use crate::{
|
|||
/// Use this method to send photos. On success, the sent [`Message`] is
|
||||
/// returned.
|
||||
pub struct SendPhoto<'a> {
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
|
@ -64,8 +63,8 @@ impl SendPhoto<'_> {
|
|||
.add("photo", self.photo);
|
||||
|
||||
network::request_multipart(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendPhoto",
|
||||
params.build(),
|
||||
)
|
||||
|
@ -74,17 +73,13 @@ impl SendPhoto<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendPhoto<'a> {
|
||||
pub(crate) fn new<C, P>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
photo: P,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, P>(bot: &'a Bot, chat_id: C, photo: P) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
P: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
photo: photo.into(),
|
||||
caption: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendPoll<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// identifier for the target chat or username of the target channel (in
|
||||
/// the format @channelusername). A native poll can't be sent to a private
|
||||
/// chat.
|
||||
|
@ -44,8 +45,8 @@ impl Request for SendPoll<'_> {
|
|||
impl SendPoll<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendPoll",
|
||||
&self,
|
||||
)
|
||||
|
@ -55,7 +56,7 @@ impl SendPoll<'_> {
|
|||
|
||||
impl<'a> SendPoll<'a> {
|
||||
pub(crate) fn new<C, Q, O>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
question: Q,
|
||||
options: O,
|
||||
|
@ -66,7 +67,7 @@ impl<'a> SendPoll<'a> {
|
|||
O: Into<Vec<String>>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
question: question.into(),
|
||||
options: options.into(),
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
|
@ -11,7 +12,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVenue<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or
|
||||
/// username of the target channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -59,8 +60,8 @@ impl Request for SendVenue<'_> {
|
|||
impl SendVenue<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendVenue",
|
||||
&self,
|
||||
)
|
||||
|
@ -70,7 +71,7 @@ impl SendVenue<'_> {
|
|||
|
||||
impl<'a> SendVenue<'a> {
|
||||
pub(crate) fn new<Lt, Lg, C, T, A>(
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
latitude: Lt,
|
||||
longitude: Lg,
|
||||
|
@ -85,7 +86,7 @@ impl<'a> SendVenue<'a> {
|
|||
A: Into<String>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
latitude: latitude.into(),
|
||||
longitude: longitude.into(),
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::network;
|
||||
use crate::requests::{Request, RequestContext, ResponseResult};
|
||||
use crate::types::{ChatId, Message, ParseMode, ReplyMarkup};
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
//TODO: add action to bot api
|
||||
///Use this method to send video files, Telegram clients support mp4 videos
|
||||
/// (other formats may be sent as Document). On success, the sent Message is
|
||||
/// returned. Bots can currently send video files of up to 50 MB in size, this
|
||||
|
@ -12,7 +14,7 @@ use crate::types::{ChatId, Message, ParseMode, ReplyMarkup};
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVideo<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -20,7 +22,7 @@ pub struct SendVideo<'a> {
|
|||
/// the Telegram servers (recommended), pass an HTTP URL as a String for
|
||||
/// Telegram to get a video from the Internet, or upload a new video using
|
||||
/// multipart/form-data. More info on Sending Files »
|
||||
pub video: String,
|
||||
pub video: InputFile,
|
||||
///Duration of sent video in seconds
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<u64>,
|
||||
|
@ -39,8 +41,7 @@ pub struct SendVideo<'a> {
|
|||
/// if the thumbnail was uploaded using multipart/form-data under
|
||||
/// <file_attach_name>. More info on Sending Files »
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumb: Option<String>,
|
||||
//InputFile or String
|
||||
pub thumb: Option<InputFile>,
|
||||
///Video caption (may also be used when resending videos by file_id),
|
||||
/// 0-1024 characters
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -78,8 +79,8 @@ impl Request for SendVideo<'_> {
|
|||
impl SendVideo<'_> {
|
||||
async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendVideo",
|
||||
&self,
|
||||
)
|
||||
|
@ -88,17 +89,13 @@ impl SendVideo<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendVideo<'a> {
|
||||
pub(crate) fn new<C, V>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
video: V,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, video: V) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<String>,
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
video: video.into(),
|
||||
duration: None,
|
||||
|
@ -123,7 +120,7 @@ impl<'a> SendVideo<'a> {
|
|||
|
||||
pub fn video<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.video = value.into();
|
||||
self
|
||||
|
@ -152,7 +149,7 @@ impl<'a> SendVideo<'a> {
|
|||
}
|
||||
pub fn thumb<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.thumb = Some(value.into());
|
||||
self
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
types::{ChatId, Message, ReplyMarkup},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
///As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1
|
||||
|
@ -12,7 +13,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVideoNote<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -20,8 +21,7 @@ pub struct SendVideoNote<'a> {
|
|||
/// exists on the Telegram servers (recommended) or upload a new video
|
||||
/// using multipart/form-data. More info on Sending Files ». Sending video
|
||||
/// notes by a URL is currently unsupported
|
||||
pub video_note: String,
|
||||
// InputFile or String
|
||||
pub video_note: InputFile,
|
||||
///Duration of sent video in seconds
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<u64>,
|
||||
|
@ -37,8 +37,7 @@ pub struct SendVideoNote<'a> {
|
|||
/// if the thumbnail was uploaded using multipart/form-data under
|
||||
/// <file_attach_name>. More info on Sending Files »
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumb: Option<String>,
|
||||
// InputFile or String
|
||||
pub thumb: Option<InputFile>,
|
||||
///Sends the message silently. Users will receive a notification with no
|
||||
/// sound.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -65,8 +64,8 @@ impl Request for SendVideoNote<'_> {
|
|||
impl SendVideoNote<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendVideoNote",
|
||||
&self,
|
||||
)
|
||||
|
@ -75,17 +74,13 @@ impl SendVideoNote<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendVideoNote<'a> {
|
||||
pub(crate) fn new<C, V>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
video_note: V,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, video_note: V) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<String>,
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
video_note: video_note.into(),
|
||||
duration: None,
|
||||
|
@ -107,7 +102,7 @@ impl<'a> SendVideoNote<'a> {
|
|||
|
||||
pub fn video_note<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.video_note = value.into();
|
||||
self
|
||||
|
@ -131,7 +126,7 @@ impl<'a> SendVideoNote<'a> {
|
|||
|
||||
pub fn thumb<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.thumb = Some(value.into());
|
||||
self
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
types::{ChatId, Message, ParseMode, ReplyMarkup},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InputFile, Message, ParseMode, ReplyMarkup},
|
||||
};
|
||||
|
||||
///Use this method to send audio files, if you want Telegram clients to display
|
||||
|
@ -15,7 +16,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVoice<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Unique identifier for the target chat or username of the target channel
|
||||
/// (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -23,8 +24,7 @@ pub struct SendVoice<'a> {
|
|||
/// on the Telegram servers (recommended), pass an HTTP URL as a String for
|
||||
/// Telegram to get a file from the Internet, or upload a new one using
|
||||
/// multipart/form-data. More info on Sending Files »
|
||||
pub voice: String,
|
||||
//InputFile or String
|
||||
pub voice: InputFile,
|
||||
/// Voice message caption, 0-1024 characters
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub caption: Option<String>,
|
||||
|
@ -62,8 +62,8 @@ impl Request for SendVoice<'_> {
|
|||
impl SendVoice<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"sendVoice",
|
||||
&self,
|
||||
)
|
||||
|
@ -72,17 +72,13 @@ impl SendVoice<'_> {
|
|||
}
|
||||
|
||||
impl<'a> SendVoice<'a> {
|
||||
pub(crate) fn new<C, V>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
voice: V,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, V>(bot: &'a Bot, chat_id: C, voice: V) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
V: Into<String>,
|
||||
V: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
voice: voice.into(),
|
||||
caption: None,
|
||||
|
@ -104,7 +100,7 @@ impl<'a> SendVoice<'a> {
|
|||
|
||||
pub fn voice<T>(mut self, value: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<InputFile>,
|
||||
{
|
||||
self.voice = value.into();
|
||||
self
|
||||
|
|
99
src/requests/set_chat_description.rs
Normal file
99
src/requests/set_chat_description.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SetChatDescription<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for SetChatDescription<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl SetChatDescription<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.bot.client(),
|
||||
&self.bot.token(),
|
||||
"setChatDescription",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SetChatDescription<'a> {
|
||||
pub(crate) fn new<C>(bot: &'a Bot, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
description: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn description<T>(mut self, description: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_new() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let method = SetChatDescription::new(&bot, chat_id);
|
||||
|
||||
let expected = r#"{"chat_id":123}"#;
|
||||
let actual =
|
||||
serde_json::to_string::<SetChatDescription>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_description() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let description = "description";
|
||||
let method =
|
||||
SetChatDescription::new(&bot, chat_id).description(description);
|
||||
|
||||
let expected = r#"{"chat_id":123,"description":"description"}"#;
|
||||
let actual =
|
||||
serde_json::to_string::<SetChatDescription>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
96
src/requests/set_chat_permissions.rs
Normal file
96
src/requests/set_chat_permissions.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, ChatPermissions, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SetChatPermissions<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
permissions: ChatPermissions,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for SetChatPermissions<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl SetChatPermissions<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"setChatPermissions",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SetChatPermissions<'a> {
|
||||
pub(crate) fn new<C, CP>(bot: &'a Bot, chat_id: C, permissions: CP) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
CP: Into<ChatPermissions>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
permissions: permissions.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn permissions<CP>(mut self, permissions: CP) -> Self
|
||||
where
|
||||
CP: Into<ChatPermissions>,
|
||||
{
|
||||
self.permissions = permissions.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let permissions = ChatPermissions {
|
||||
can_send_messages: Some(true),
|
||||
can_send_media_messages: None,
|
||||
can_send_polls: None,
|
||||
can_send_other_messages: None,
|
||||
can_add_web_page_previews: None,
|
||||
can_change_info: None,
|
||||
can_invite_users: None,
|
||||
can_pin_messages: None,
|
||||
};
|
||||
let method = SetChatPermissions::new(&bot, chat_id, permissions);
|
||||
|
||||
let expected =
|
||||
r#"{"chat_id":123,"permissions":{"can_send_messages":true}}"#;
|
||||
let actual =
|
||||
serde_json::to_string::<SetChatPermissions>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
90
src/requests/set_chat_photo.rs
Normal file
90
src/requests/set_chat_photo.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{form_builder::FormBuilder, Request, ResponseResult},
|
||||
types::{ChatId, InputFile, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SetChatPhoto<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
photo: InputFile,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for SetChatPhoto<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl SetChatPhoto<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
let params = FormBuilder::new()
|
||||
.add("chat_id", self.chat_id)
|
||||
.add("photo", self.photo);
|
||||
|
||||
network::request_multipart(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"setChatPhoto",
|
||||
params.build(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SetChatPhoto<'a> {
|
||||
pub(crate) fn new<C, P>(bot: &'a Bot, chat_id: C, photo: P) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
P: Into<InputFile>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
photo: photo.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn photo<P>(mut self, photo: P) -> Self
|
||||
where
|
||||
P: Into<InputFile>,
|
||||
{
|
||||
self.photo = photo.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let photo_url = "https://some_url".to_string();
|
||||
let method =
|
||||
SetChatPhoto::new(&bot, chat_id, InputFile::Url(photo_url));
|
||||
|
||||
let expected = r#"{"chat_id":123,"photo":"https://some_url"}"#;
|
||||
let actual = serde_json::to_string::<SetChatPhoto>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
81
src/requests/set_chat_sticker_set.rs
Normal file
81
src/requests/set_chat_sticker_set.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
/// Use this method to set a new group sticker set for a supergroup. The bot
|
||||
/// must be an administrator in the chat for this to work and must have the
|
||||
/// appropriate admin rights. Use the field can_set_sticker_set optionally
|
||||
/// returned in getChat requests to check if the bot can use this method.
|
||||
/// Returns True on success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SetChatStickerSet<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
/// Unique identifier for the target chat or username of the target
|
||||
/// supergroup (in the format @supergroupusername)
|
||||
chat_id: ChatId,
|
||||
|
||||
/// Name of the sticker set to be set as the group sticker set
|
||||
sticker_set_name: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for SetChatStickerSet<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl SetChatStickerSet<'_> {
|
||||
async fn send(&self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"setChatStickerSet",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SetChatStickerSet<'a> {
|
||||
pub(crate) fn new<C, S>(
|
||||
bot: &'a Bot,
|
||||
chat_id: C,
|
||||
sticker_set_name: S,
|
||||
) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
sticker_set_name: sticker_set_name.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sticker_set_name<S>(mut self, value: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.sticker_set_name = value.into();
|
||||
self
|
||||
}
|
||||
}
|
85
src/requests/set_chat_title.rs
Normal file
85
src/requests/set_chat_title.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SetChatTitle<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
bot: &'a Bot,
|
||||
|
||||
chat_id: ChatId,
|
||||
title: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Request for SetChatTitle<'_> {
|
||||
type Output = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::Output> {
|
||||
self.send().await
|
||||
}
|
||||
}
|
||||
|
||||
impl SetChatTitle<'_> {
|
||||
async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.bot.client(),
|
||||
&self.bot.token(),
|
||||
"setChatTitle",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SetChatTitle<'a> {
|
||||
pub(crate) fn new<C, T>(bot: &'a Bot, chat_id: C, title: T) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
T: Into<String>,
|
||||
{
|
||||
Self {
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
title: title.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<C>(mut self, chat_id: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title<C>(mut self, title: C) -> Self
|
||||
where
|
||||
C: Into<String>,
|
||||
{
|
||||
self.title = title.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let bot = Bot::new("token");
|
||||
let chat_id = 123;
|
||||
let title = "title";
|
||||
let method = SetChatTitle::new(&bot, chat_id, title);
|
||||
|
||||
let expected = r#"{"chat_id":123,"title":"title"}"#;
|
||||
let actual = serde_json::to_string::<SetChatTitle>(&method).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, InlineKeyboardMarkup, Message},
|
||||
};
|
||||
|
||||
|
@ -12,7 +13,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct StopMessageLiveLocation<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
/// Required if inline_message_id is not specified. Unique identifier for
|
||||
/// the target chat or username of the target channel (in the format
|
||||
/// @channelusername)
|
||||
|
@ -44,8 +45,8 @@ impl Request for StopMessageLiveLocation<'_> {
|
|||
impl StopMessageLiveLocation<'_> {
|
||||
pub async fn send(self) -> ResponseResult<Message> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"stopMessageLiveLocation",
|
||||
&self,
|
||||
)
|
||||
|
@ -54,9 +55,9 @@ impl StopMessageLiveLocation<'_> {
|
|||
}
|
||||
|
||||
impl<'a> StopMessageLiveLocation<'a> {
|
||||
pub(crate) fn new(ctx: RequestContext<'a>) -> Self {
|
||||
pub(crate) fn new(bot: &'a Bot) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: None,
|
||||
message_id: None,
|
||||
inline_message_id: None,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::ChatId,
|
||||
};
|
||||
|
||||
|
@ -13,7 +14,7 @@ use crate::{
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct UnbanChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
///Unique identifier for the target group or username of the target
|
||||
/// supergroup or channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
|
@ -33,8 +34,8 @@ impl Request for UnbanChatMember<'_> {
|
|||
impl UnbanChatMember<'_> {
|
||||
pub async fn send(self) -> ResponseResult<bool> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"unbanChatMember",
|
||||
&self,
|
||||
)
|
||||
|
@ -43,17 +44,13 @@ impl UnbanChatMember<'_> {
|
|||
}
|
||||
|
||||
impl<'a> UnbanChatMember<'a> {
|
||||
pub(crate) fn new<C, U>(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: C,
|
||||
user_id: U,
|
||||
) -> Self
|
||||
pub(crate) fn new<C, U>(bot: &'a Bot, chat_id: C, user_id: U) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
U: Into<i32>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: chat_id.into(),
|
||||
user_id: user_id.into(),
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
bot::Bot,
|
||||
network,
|
||||
requests::{Request, RequestContext, ResponseResult},
|
||||
requests::{Request, ResponseResult},
|
||||
types::{ChatId, True},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct UnpinChatMessage<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
pub ctx: RequestContext<'a>,
|
||||
bot: &'a Bot,
|
||||
|
||||
pub chat_id: ChatId,
|
||||
}
|
||||
|
@ -26,8 +27,8 @@ impl Request for UnpinChatMessage<'_> {
|
|||
impl UnpinChatMessage<'_> {
|
||||
pub async fn send(self) -> ResponseResult<True> {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
self.bot.client(),
|
||||
self.bot.token(),
|
||||
"unpinChatMessage",
|
||||
&self,
|
||||
)
|
||||
|
@ -36,12 +37,12 @@ impl UnpinChatMessage<'_> {
|
|||
}
|
||||
|
||||
impl<'a> UnpinChatMessage<'a> {
|
||||
pub(crate) fn new<C>(ctx: RequestContext<'a>, value: C) -> Self
|
||||
pub(crate) fn new<C>(bot: &'a Bot, value: C) -> Self
|
||||
where
|
||||
C: Into<ChatId>,
|
||||
{
|
||||
Self {
|
||||
ctx,
|
||||
bot,
|
||||
chat_id: value.into(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Serialize, Clone)]
|
||||
pub struct ChatPermissions {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_send_messages: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_send_media_messages: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_send_polls: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_send_other_messages: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_add_web_page_previews: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_change_info: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_invite_users: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub can_pin_messages: Option<bool>,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::passport_file::PassportFile;
|
||||
use super::PassportFile;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct EncryptedPassportElement {
|
||||
|
|
|
@ -45,7 +45,7 @@ pub enum InlineKeyboardButtonKind {
|
|||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// use async_telegram_bot::types::InlineKeyboardButton;
|
||||
/// use telebofr::types::InlineKeyboardButton;
|
||||
///
|
||||
/// let url_button = InlineKeyboardButton::url(
|
||||
/// "Text".to_string(),
|
||||
|
|
|
@ -16,9 +16,7 @@ pub struct InlineKeyboardMarkup {
|
|||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// use async_telegram_bot::types::{
|
||||
/// InlineKeyboardButton, InlineKeyboardMarkup,
|
||||
/// };
|
||||
/// use telebofr::types::{InlineKeyboardButton, InlineKeyboardMarkup};
|
||||
///
|
||||
/// let url_button = InlineKeyboardButton::url(
|
||||
/// "text".to_string(),
|
||||
|
|
|
@ -49,9 +49,8 @@ pub enum InlineQueryResult {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::inline_keyboard_markup::InlineKeyboardMarkup;
|
||||
use crate::types::parse_mode::ParseMode;
|
||||
use crate::types::{
|
||||
inline_keyboard_markup::InlineKeyboardMarkup, parse_mode::ParseMode,
|
||||
InlineQueryResult, InlineQueryResultCachedAudio, InputMessageContent,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// This object represents one button of the reply keyboard. For simple text
|
||||
/// This object represents one button of the reply keyboard. For filter text
|
||||
/// buttons String can be used instead of this object to specify text of the
|
||||
/// button. Optional fields are mutually exclusive.
|
||||
#[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
use crate::types::{Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup, Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker, SuccessfulPayment, User, Venue, Video, VideoNote, Voice, True};
|
||||
use crate::types::{
|
||||
Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup,
|
||||
Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker,
|
||||
SuccessfulPayment, True, User, Venue, Video, VideoNote, Voice,
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||
pub struct Message {
|
||||
|
@ -10,7 +14,6 @@ pub struct Message {
|
|||
pub kind: MessageKind,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum MessageKind {
|
||||
|
@ -188,23 +191,23 @@ mod getters {
|
|||
use std::ops::Deref;
|
||||
|
||||
use crate::types::{
|
||||
self, Message, Sender, User, ForwardedFrom, Chat, MessageEntity,
|
||||
PhotoSize, True,
|
||||
self,
|
||||
message::{
|
||||
MessageKind::{
|
||||
Common, NewChatMembers, LeftChatMember, NewChatTitle,
|
||||
NewChatPhoto, DeleteChatPhoto, GroupChatCreated,
|
||||
ChannelChatCreated, Migrate, Invoice, SuccessfulPayment,
|
||||
ConnectedWebsite, PassportData
|
||||
},
|
||||
ForwardKind::{ChannelForward, NonChannelForward, Origin},
|
||||
MediaKind::{
|
||||
Text, Video, Photo, Animation, Audio, Document, Voice, Game,
|
||||
Sticker, VideoNote, Contact, Location, Poll, Venue
|
||||
Animation, Audio, Contact, Document, Game, Location, Photo,
|
||||
Poll, Sticker, Text, Venue, Video, VideoNote, Voice,
|
||||
},
|
||||
MessageKind::{
|
||||
ChannelChatCreated, Common, ConnectedWebsite, DeleteChatPhoto,
|
||||
GroupChatCreated, Invoice, LeftChatMember, Migrate,
|
||||
NewChatMembers, NewChatPhoto, NewChatTitle, PassportData,
|
||||
Pinned, SuccessfulPayment, SupergroupChatCreated,
|
||||
},
|
||||
ForwardKind::{NonChannelForward, ChannelForward, Origin}
|
||||
},
|
||||
Chat, ForwardedFrom, Message, MessageEntity, PhotoSize, Sender, True,
|
||||
User,
|
||||
};
|
||||
use crate::types::message::MessageKind::{SupergroupChatCreated, Pinned};
|
||||
|
||||
/// Getters for [Message] fields from [telegram docs].
|
||||
///
|
||||
|
@ -223,49 +226,67 @@ mod getters {
|
|||
/// `forward_sender_name`
|
||||
pub fn forward_from(&self) -> Option<&ForwardedFrom> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: NonChannelForward { from, .. }, .. } =>
|
||||
Some(from),
|
||||
Common {
|
||||
forward_kind: NonChannelForward { from, .. },
|
||||
..
|
||||
} => Some(from),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward_from_chat(&self) -> Option<&Chat> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: ChannelForward { chat, .. }, .. } =>
|
||||
Some(chat),
|
||||
Common {
|
||||
forward_kind: ChannelForward { chat, .. },
|
||||
..
|
||||
} => Some(chat),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward_from_message_id(&self) -> Option<&i32> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: ChannelForward { message_id, .. }, .. } =>
|
||||
Some(message_id),
|
||||
Common {
|
||||
forward_kind: ChannelForward { message_id, .. },
|
||||
..
|
||||
} => Some(message_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward_signature(&self) -> Option<&str> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: ChannelForward { signature, .. }, .. } =>
|
||||
signature.as_ref().map(Deref::deref),
|
||||
Common {
|
||||
forward_kind: ChannelForward { signature, .. },
|
||||
..
|
||||
} => signature.as_ref().map(Deref::deref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward_date(&self) -> Option<&i32> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: ChannelForward { date, .. }, .. } |
|
||||
Common { forward_kind: NonChannelForward { date, .. }, .. } =>
|
||||
Some(date),
|
||||
Common {
|
||||
forward_kind: ChannelForward { date, .. },
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
forward_kind: NonChannelForward { date, .. },
|
||||
..
|
||||
} => Some(date),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reply_to_message(&self) -> Option<&Message> {
|
||||
match &self.kind {
|
||||
Common { forward_kind: Origin { reply_to_message, .. }, .. } =>
|
||||
reply_to_message.as_ref().map(Deref::deref),
|
||||
Common {
|
||||
forward_kind:
|
||||
Origin {
|
||||
reply_to_message, ..
|
||||
},
|
||||
..
|
||||
} => reply_to_message.as_ref().map(Deref::deref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -279,104 +300,172 @@ mod getters {
|
|||
|
||||
pub fn media_group_id(&self) -> Option<&str> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Video { media_group_id, .. }, .. } |
|
||||
Common { media_kind: Photo { media_group_id, .. }, .. } =>
|
||||
media_group_id.as_ref().map(Deref::deref),
|
||||
Common {
|
||||
media_kind: Video { media_group_id, .. },
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind: Photo { media_group_id, .. },
|
||||
..
|
||||
} => media_group_id.as_ref().map(Deref::deref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(&self) -> Option<&str> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Text { text, .. }, .. } => Some(text),
|
||||
Common {
|
||||
media_kind: Text { text, .. },
|
||||
..
|
||||
} => Some(text),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entities(&self) -> Option<&[MessageEntity]> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Text { entities, .. }, .. } =>
|
||||
Some(entities),
|
||||
Common {
|
||||
media_kind: Text { entities, .. },
|
||||
..
|
||||
} => Some(entities),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn caption_entities(&self) -> Option<&[MessageEntity]> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Animation { caption_entities, .. }, .. } |
|
||||
Common { media_kind: Audio { caption_entities, .. }, .. } |
|
||||
Common { media_kind: Document { caption_entities, .. }, .. } |
|
||||
Common { media_kind: Photo { caption_entities, .. }, .. } |
|
||||
Common { media_kind: Video { caption_entities, .. }, .. } |
|
||||
Common { media_kind: Voice { caption_entities, .. }, .. } =>
|
||||
Some(caption_entities),
|
||||
Common {
|
||||
media_kind:
|
||||
Animation {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind:
|
||||
Audio {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind:
|
||||
Document {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind:
|
||||
Photo {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind:
|
||||
Video {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
}
|
||||
| Common {
|
||||
media_kind:
|
||||
Voice {
|
||||
caption_entities, ..
|
||||
},
|
||||
..
|
||||
} => Some(caption_entities),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn audio(&self) -> Option<&types::Audio> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Audio { audio, .. }, .. } => Some(audio),
|
||||
Common {
|
||||
media_kind: Audio { audio, .. },
|
||||
..
|
||||
} => Some(audio),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn document(&self) -> Option<&types::Document> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Document { document, .. }, .. } =>
|
||||
Some(document),
|
||||
Common {
|
||||
media_kind: Document { document, .. },
|
||||
..
|
||||
} => Some(document),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn animation(&self) -> Option<&types::Animation> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Animation { animation, .. }, .. } =>
|
||||
Some(animation),
|
||||
Common {
|
||||
media_kind: Animation { animation, .. },
|
||||
..
|
||||
} => Some(animation),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game(&self) -> Option<&types::Game> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Game { game, .. }, .. } => Some(game),
|
||||
Common {
|
||||
media_kind: Game { game, .. },
|
||||
..
|
||||
} => Some(game),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn photo(&self) -> Option<&[PhotoSize]> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Photo { photo, .. }, .. } => Some(photo),
|
||||
Common {
|
||||
media_kind: Photo { photo, .. },
|
||||
..
|
||||
} => Some(photo),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sticker(&self) -> Option<&types::Sticker> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Sticker { sticker, .. }, .. } =>
|
||||
Some(sticker),
|
||||
Common {
|
||||
media_kind: Sticker { sticker, .. },
|
||||
..
|
||||
} => Some(sticker),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn video(&self) -> Option<&types::Video> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Video { video, .. }, .. } => Some(video),
|
||||
Common {
|
||||
media_kind: Video { video, .. },
|
||||
..
|
||||
} => Some(video),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn voice(&self) -> Option<&types::Voice> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Voice { voice, .. }, .. } => Some(voice),
|
||||
Common {
|
||||
media_kind: Voice { voice, .. },
|
||||
..
|
||||
} => Some(voice),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn video_note(&self) -> Option<&types::VideoNote> {
|
||||
match &self.kind {
|
||||
Common { media_kind: VideoNote { video_note, .. }, .. } =>
|
||||
Some(video_note),
|
||||
Common {
|
||||
media_kind: VideoNote { video_note, .. },
|
||||
..
|
||||
} => Some(video_note),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -384,12 +473,14 @@ mod getters {
|
|||
pub fn caption(&self) -> Option<&str> {
|
||||
match &self.kind {
|
||||
Common { media_kind, .. } => match media_kind {
|
||||
Animation { caption, ..} |
|
||||
Audio { caption, ..} |
|
||||
Document { caption, ..} |
|
||||
Photo { caption, ..} |
|
||||
Video { caption, ..} |
|
||||
Voice { caption, ..} => caption.as_ref().map(Deref::deref),
|
||||
Animation { caption, .. }
|
||||
| Audio { caption, .. }
|
||||
| Document { caption, .. }
|
||||
| Photo { caption, .. }
|
||||
| Video { caption, .. }
|
||||
| Voice { caption, .. } => {
|
||||
caption.as_ref().map(Deref::deref)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
|
@ -398,29 +489,40 @@ mod getters {
|
|||
|
||||
pub fn contact(&self) -> Option<&types::Contact> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Contact { contact }, .. } => Some(contact),
|
||||
Common {
|
||||
media_kind: Contact { contact },
|
||||
..
|
||||
} => Some(contact),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn location(&self) -> Option<&types::Location> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Location { location, .. }, .. } =>
|
||||
Some(location),
|
||||
Common {
|
||||
media_kind: Location { location, .. },
|
||||
..
|
||||
} => Some(location),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn venue(&self) -> Option<&types::Venue> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Venue { venue, .. }, .. } => Some(venue),
|
||||
Common {
|
||||
media_kind: Venue { venue, .. },
|
||||
..
|
||||
} => Some(venue),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(&self) -> Option<&types::Poll> {
|
||||
match &self.kind {
|
||||
Common { media_kind: Poll { poll, .. }, .. } => Some(poll),
|
||||
Common {
|
||||
media_kind: Poll { poll, .. },
|
||||
..
|
||||
} => Some(poll),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -457,47 +559,55 @@ mod getters {
|
|||
// mb smt like `is_delete_chat_photo(&self) -> bool`?
|
||||
pub fn delete_chat_photo(&self) -> Option<True> {
|
||||
match &self.kind {
|
||||
DeleteChatPhoto { delete_chat_photo } =>
|
||||
Some(*delete_chat_photo),
|
||||
DeleteChatPhoto { delete_chat_photo } => {
|
||||
Some(*delete_chat_photo)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn group_chat_created(&self) -> Option<True> {
|
||||
match &self.kind {
|
||||
GroupChatCreated { group_chat_created } =>
|
||||
Some(*group_chat_created),
|
||||
GroupChatCreated { group_chat_created } => {
|
||||
Some(*group_chat_created)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn super_group_chat_created(&self) -> Option<True> {
|
||||
match &self.kind {
|
||||
SupergroupChatCreated { supergroup_chat_created } =>
|
||||
Some(*supergroup_chat_created),
|
||||
SupergroupChatCreated {
|
||||
supergroup_chat_created,
|
||||
} => Some(*supergroup_chat_created),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_chat_created(&self) -> Option<True> {
|
||||
match &self.kind {
|
||||
ChannelChatCreated { channel_chat_created } =>
|
||||
Some(*channel_chat_created),
|
||||
ChannelChatCreated {
|
||||
channel_chat_created,
|
||||
} => Some(*channel_chat_created),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn migrate_to_chat_id(&self) -> Option<&i64> {
|
||||
match &self.kind {
|
||||
Migrate { migrate_to_chat_id, .. } => Some(migrate_to_chat_id),
|
||||
Migrate {
|
||||
migrate_to_chat_id, ..
|
||||
} => Some(migrate_to_chat_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn migrate_from_chat_id(&self) -> Option<&i64> {
|
||||
match &self.kind {
|
||||
Migrate { migrate_from_chat_id, .. } =>
|
||||
Some(migrate_from_chat_id),
|
||||
Migrate {
|
||||
migrate_from_chat_id,
|
||||
..
|
||||
} => Some(migrate_from_chat_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -516,25 +626,24 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn successful_payment(&self) -> Option<&types::SuccessfulPayment> {
|
||||
match &self.kind {
|
||||
SuccessfulPayment { successful_payment } =>
|
||||
Some(successful_payment),
|
||||
SuccessfulPayment { successful_payment } => {
|
||||
Some(successful_payment)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn connected_website(&self) -> Option<&str> {
|
||||
match &self.kind {
|
||||
ConnectedWebsite { connected_website } =>
|
||||
Some(connected_website),
|
||||
ConnectedWebsite { connected_website } => {
|
||||
Some(connected_website)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn passport_data(&self) -> Option<&types::PassportData> {
|
||||
match &self.kind {
|
||||
PassportData { passport_data } => Some(passport_data),
|
||||
|
@ -542,7 +651,6 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn reply_markup(&self) -> Option<&types::InlineKeyboardMarkup> {
|
||||
match &self.kind {
|
||||
Common { reply_markup, .. } => reply_markup.as_ref(),
|
||||
|
@ -552,7 +660,6 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::from_str;
|
||||
|
|
174
src/types/mod.rs
174
src/types/mod.rs
|
@ -1,92 +1,87 @@
|
|||
//! Raw API structures.
|
||||
//! API types.
|
||||
|
||||
pub use self::{
|
||||
animation::Animation,
|
||||
audio::Audio,
|
||||
callback_game::CallbackGame,
|
||||
callback_query::CallbackQuery,
|
||||
chat::{Chat, ChatKind, NonPrivateChatKind},
|
||||
chat_action::ChatAction,
|
||||
chat_id::ChatId,
|
||||
chat_member::{ChatMember, ChatMemberStatus},
|
||||
chat_permissions::ChatPermissions,
|
||||
chat_photo::ChatPhoto,
|
||||
chosen_inline_result::ChosenInlineResult,
|
||||
contact::Contact,
|
||||
document::Document,
|
||||
encrypted_credintials::EncryptedCredentials,
|
||||
encrypted_passport_element::{
|
||||
EncryptedPassportElement, EncryptedPassportElementKind,
|
||||
},
|
||||
file::File,
|
||||
force_reply::ForceReply,
|
||||
game::Game,
|
||||
game_high_score::GameHighScore,
|
||||
inline_keyboard_button::{InlineKeyboardButton, InlineKeyboardButtonKind},
|
||||
inline_keyboard_markup::InlineKeyboardMarkup,
|
||||
inline_query::InlineQuery,
|
||||
inline_query_result::InlineQueryResult,
|
||||
inline_query_result_article::InlineQueryResultArticle,
|
||||
inline_query_result_audio::InlineQueryResultAudio,
|
||||
inline_query_result_cached_audio::InlineQueryResultCachedAudio,
|
||||
inline_query_result_cached_document::InlineQueryResultCachedDocument,
|
||||
inline_query_result_cached_gif::InlineQueryResultCachedGif,
|
||||
inline_query_result_cached_mpeg4_gif::InlineQueryResultCachedMpeg4Gif,
|
||||
inline_query_result_cached_photo::InlineQueryResultCachedPhoto,
|
||||
inline_query_result_cached_sticker::InlineQueryResultCachedSticker,
|
||||
inline_query_result_cached_video::InlineQueryResultCachedVideo,
|
||||
inline_query_result_cached_voice::InlineQueryResultCachedVoice,
|
||||
inline_query_result_contact::InlineQueryResultContact,
|
||||
inline_query_result_document::InlineQueryResultDocument,
|
||||
inline_query_result_game::InlineQueryResultGame,
|
||||
inline_query_result_gif::InlineQueryResultGif,
|
||||
inline_query_result_location::InlineQueryResultLocation,
|
||||
inline_query_result_mpeg4_gif::InlineQueryResultMpeg4Gif,
|
||||
inline_query_result_photo::InlineQueryResultPhoto,
|
||||
inline_query_result_venue::InlineQueryResultVenue,
|
||||
inline_query_result_video::InlineQueryResultVideo,
|
||||
inline_query_result_voice::InlineQueryResultVoice,
|
||||
input_file::InputFile,
|
||||
input_media::InputMedia,
|
||||
input_message_content::InputMessageContent,
|
||||
invoice::Invoice,
|
||||
keyboard_button::KeyboardButton,
|
||||
label_price::LabeledPrice,
|
||||
location::Location,
|
||||
login_url::LoginUrl,
|
||||
mask_position::MaskPosition,
|
||||
message::{
|
||||
ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind, Sender,
|
||||
},
|
||||
message_entity::MessageEntity,
|
||||
order_info::OrderInfo,
|
||||
parse_mode::ParseMode,
|
||||
passport_data::PassportData,
|
||||
passport_file::PassportFile,
|
||||
photo_size::PhotoSize,
|
||||
poll::{Poll, PollOption},
|
||||
pre_checkout_query::PreCheckoutQuery,
|
||||
reply_keyboard_markup::ReplyKeyboardMarkup,
|
||||
reply_keyboard_remove::ReplyKeyboardRemove,
|
||||
reply_markup::ReplyMarkup,
|
||||
response_parameters::ResponseParameters,
|
||||
send_invoice::SendInvoice,
|
||||
shipping_address::ShippingAddress,
|
||||
shipping_option::ShippingOption,
|
||||
shipping_query::ShippingQuery,
|
||||
sticker::Sticker,
|
||||
sticker_set::StickerSet,
|
||||
successful_payment::SuccessfulPayment,
|
||||
unit_true::True,
|
||||
update::{Update, UpdateKind},
|
||||
user::User,
|
||||
user_profile_photos::UserProfilePhotos,
|
||||
venue::Venue,
|
||||
video::Video,
|
||||
video_note::VideoNote,
|
||||
voice::Voice,
|
||||
webhook_info::WebhookInfo,
|
||||
};
|
||||
pub use animation::*;
|
||||
pub use audio::*;
|
||||
pub use callback_game::*;
|
||||
pub use callback_query::*;
|
||||
pub use chat::*;
|
||||
pub use chat_action::*;
|
||||
pub use chat_id::*;
|
||||
pub use chat_member::*;
|
||||
pub use chat_permissions::*;
|
||||
pub use chat_photo::*;
|
||||
pub use chosen_inline_result::*;
|
||||
pub use contact::*;
|
||||
pub use document::*;
|
||||
pub use encrypted_credentials::*;
|
||||
pub use encrypted_passport_element::*;
|
||||
pub use file::*;
|
||||
pub use force_reply::*;
|
||||
pub use game::*;
|
||||
pub use game_high_score::*;
|
||||
pub use inline_keyboard_button::*;
|
||||
pub use inline_keyboard_markup::*;
|
||||
pub use inline_query::*;
|
||||
pub use inline_query_result::*;
|
||||
pub use inline_query_result_article::*;
|
||||
pub use inline_query_result_audio::*;
|
||||
pub use inline_query_result_cached_audio::*;
|
||||
pub use inline_query_result_cached_document::*;
|
||||
pub use inline_query_result_cached_gif::*;
|
||||
pub use inline_query_result_cached_mpeg4_gif::*;
|
||||
pub use inline_query_result_cached_photo::*;
|
||||
pub use inline_query_result_cached_sticker::*;
|
||||
pub use inline_query_result_cached_video::*;
|
||||
pub use inline_query_result_cached_voice::*;
|
||||
pub use inline_query_result_contact::*;
|
||||
pub use inline_query_result_document::*;
|
||||
pub use inline_query_result_game::*;
|
||||
pub use inline_query_result_gif::*;
|
||||
pub use inline_query_result_location::*;
|
||||
pub use inline_query_result_mpeg4_gif::*;
|
||||
pub use inline_query_result_photo::*;
|
||||
pub use inline_query_result_venue::*;
|
||||
pub use inline_query_result_video::*;
|
||||
pub use inline_query_result_voice::*;
|
||||
pub use input_file::*;
|
||||
pub use input_media::*;
|
||||
pub use input_message_content::*;
|
||||
pub use invoice::*;
|
||||
pub use keyboard_button::*;
|
||||
pub use label_price::*;
|
||||
pub use location::*;
|
||||
pub use login_url::*;
|
||||
pub use mask_position::*;
|
||||
pub use message::*;
|
||||
pub use message_entity::*;
|
||||
pub use order_info::*;
|
||||
pub use parse_mode::*;
|
||||
pub use passport_data::*;
|
||||
pub use passport_file::*;
|
||||
pub use photo_size::*;
|
||||
pub use poll::*;
|
||||
pub use pre_checkout_query::*;
|
||||
pub use reply_keyboard_markup::*;
|
||||
pub use reply_keyboard_remove::*;
|
||||
pub use reply_markup::*;
|
||||
pub use response_parameters::*;
|
||||
pub use send_invoice::*;
|
||||
pub use shipping_address::*;
|
||||
pub use shipping_option::*;
|
||||
pub use shipping_query::*;
|
||||
pub use sticker::*;
|
||||
pub use sticker_set::*;
|
||||
pub use successful_payment::*;
|
||||
pub use unit_false::*;
|
||||
pub use unit_true::*;
|
||||
pub use update::*;
|
||||
pub use user::*;
|
||||
pub use user_profile_photos::*;
|
||||
pub use venue::*;
|
||||
pub use video::*;
|
||||
pub use video_note::*;
|
||||
pub use voice::*;
|
||||
pub use webhook_info::*;
|
||||
|
||||
mod animation;
|
||||
mod audio;
|
||||
|
@ -134,6 +129,7 @@ mod shipping_query;
|
|||
mod sticker;
|
||||
mod sticker_set;
|
||||
mod successful_payment;
|
||||
mod unit_false;
|
||||
mod unit_true;
|
||||
mod update;
|
||||
mod user;
|
||||
|
@ -167,7 +163,7 @@ mod inline_query_result_venue;
|
|||
mod inline_query_result_video;
|
||||
mod inline_query_result_voice;
|
||||
|
||||
mod encrypted_credintials;
|
||||
mod encrypted_credentials;
|
||||
mod encrypted_passport_element;
|
||||
mod passport_data;
|
||||
mod passport_file;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use super::encrypted_credintials::EncryptedCredentials;
|
||||
use super::encrypted_passport_element::EncryptedPassportElement;
|
||||
use super::{EncryptedCredentials, EncryptedPassportElement};
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
||||
pub struct PassportData {
|
||||
|
|
80
src/types/unit_false.rs
Normal file
80
src/types/unit_false.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct False;
|
||||
|
||||
impl std::convert::TryFrom<bool> for False {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: bool) -> Result<Self, Self::Error> {
|
||||
#[allow(clippy::match_bool)]
|
||||
match value {
|
||||
true => Err(()),
|
||||
false => Ok(False),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for False {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_bool(FalseVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct FalseVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for FalseVisitor {
|
||||
type Value = False;
|
||||
|
||||
fn expecting(
|
||||
&self,
|
||||
formatter: &mut std::fmt::Formatter,
|
||||
) -> std::fmt::Result {
|
||||
write!(formatter, "bool, equal to `false`")
|
||||
}
|
||||
|
||||
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
#[allow(clippy::match_bool)]
|
||||
match value {
|
||||
true => Err(E::custom("expected `false`, found `true`")),
|
||||
false => Ok(False),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for False {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use super::False;
|
||||
|
||||
#[test]
|
||||
fn unit_false_de() {
|
||||
let json = "false";
|
||||
let expected = False;
|
||||
let actual = from_str(json).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit_false_se() {
|
||||
let actual = to_string(&False).unwrap();
|
||||
let expected = "false";
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde::{
|
||||
de::{self, Deserialize, Deserializer, Visitor},
|
||||
ser::{Serialize, Serializer},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct True;
|
||||
|
||||
impl std::convert::TryFrom<bool> for True {
|
||||
|
|
Loading…
Add table
Reference in a new issue