use serde::Serialize;
use serde_json::Value;
use crate::{Error, Result};
use crate::types::validation::{
positive_i64 as validate_positive_i64, required_string as validate_required_string,
string_id as validate_string_id,
};
use super::AdvancedRequest;
trait GeneratedValidate {
fn validate_generated(&self) -> Result<()>;
}
impl GeneratedValidate for crate::types::payment::LabeledPrice {
fn validate_generated(&self) -> Result<()> {
self.validate()
}
}
impl GeneratedValidate for crate::types::payment::ShippingOption {
fn validate_generated(&self) -> Result<()> {
self.validate()
}
}
impl GeneratedValidate for crate::types::telegram::PassportElementError {
fn validate_generated(&self) -> Result<()> {
self.validate()
}
}
fn validate_items<T: GeneratedValidate>(values: &[T]) -> Result<()> {
for value in values {
value.validate_generated()?;
}
Ok(())
}
fn validate_required_items<T: GeneratedValidate>(field: &str, values: &[T]) -> Result<()> {
if values.is_empty() {
return Err(Error::InvalidRequest {
reason: format!("{field} cannot be empty"),
});
}
validate_items(values)
}
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedSendInvoiceRequest {
pub chat_id: crate::types::common::ChatId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_thread_id: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub direct_messages_topic_id: Option<i64>,
pub title: String,
pub description: String,
pub payload: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_token: Option<String>,
pub currency: String,
pub prices: Vec<crate::types::payment::LabeledPrice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tip_amount: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub suggested_tip_amounts: Option<Vec<i64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_parameter: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_data: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_size: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_width: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_height: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_name: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_phone_number: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_email: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_shipping_address: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub send_phone_number_to_provider: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub send_email_to_provider: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_flexible: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disable_notification: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub protect_content: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allow_paid_broadcast: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_effect_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub suggested_post_parameters: Option<crate::types::telegram::SuggestedPostParameters>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reply_parameters: Option<crate::types::telegram::ReplyParameters>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reply_markup: Option<crate::types::telegram::InlineKeyboardMarkup>,
}
impl AdvancedSendInvoiceRequest {
pub fn new(
chat_id: impl Into<crate::types::common::ChatId>,
title: impl Into<String>,
description: impl Into<String>,
payload: impl Into<String>,
currency: impl Into<String>,
prices: Vec<crate::types::payment::LabeledPrice>,
) -> Self {
Self {
chat_id: chat_id.into(),
message_thread_id: None,
direct_messages_topic_id: None,
title: title.into(),
description: description.into(),
payload: payload.into(),
provider_token: None,
currency: currency.into(),
prices,
max_tip_amount: None,
suggested_tip_amounts: None,
start_parameter: None,
provider_data: None,
photo_url: None,
photo_size: None,
photo_width: None,
photo_height: None,
need_name: None,
need_phone_number: None,
need_email: None,
need_shipping_address: None,
send_phone_number_to_provider: None,
send_email_to_provider: None,
is_flexible: None,
disable_notification: None,
protect_content: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
reply_parameters: None,
reply_markup: None,
}
}
}
impl AdvancedRequest for AdvancedSendInvoiceRequest {
type Response = crate::types::message::Message;
const METHOD: &'static str = "sendInvoice";
fn validate(&self) -> Result<()> {
self.chat_id.validate()?;
if let Some(value) = self.message_thread_id {
validate_positive_i64("message_thread_id", value)?;
}
if let Some(value) = self.direct_messages_topic_id {
validate_positive_i64("direct_messages_topic_id", value)?;
}
validate_required_string("title", &self.title)?;
validate_required_string("description", &self.description)?;
validate_required_string("payload", &self.payload)?;
validate_required_string("currency", &self.currency)?;
validate_required_items::<crate::types::payment::LabeledPrice>("prices", &self.prices)?;
if let Some(value) = self.max_tip_amount {
validate_positive_i64("max_tip_amount", value)?;
}
if let Some(value) = self.photo_size {
validate_positive_i64("photo_size", value)?;
}
if let Some(value) = self.photo_width {
validate_positive_i64("photo_width", value)?;
}
if let Some(value) = self.photo_height {
validate_positive_i64("photo_height", value)?;
}
if let Some(value) = self.message_effect_id.as_deref() {
validate_string_id("message_effect_id", value)?;
}
if let Some(value) = self.suggested_post_parameters.as_ref() {
value.validate()?;
}
if let Some(value) = self.reply_parameters.as_ref() {
value.validate()?;
}
if let Some(value) = self.reply_markup.as_ref() {
value.validate()?;
}
Ok(())
}
}
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedCreateInvoiceLinkRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub business_connection_id: Option<String>,
pub title: String,
pub description: String,
pub payload: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_token: Option<String>,
pub currency: String,
pub prices: Vec<crate::types::payment::LabeledPrice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subscription_period: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tip_amount: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub suggested_tip_amounts: Option<Vec<i64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_data: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_size: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_width: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo_height: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_name: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_phone_number: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_email: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub need_shipping_address: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub send_phone_number_to_provider: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub send_email_to_provider: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub is_flexible: Option<bool>,
}
impl AdvancedCreateInvoiceLinkRequest {
pub fn new(
title: impl Into<String>,
description: impl Into<String>,
payload: impl Into<String>,
currency: impl Into<String>,
prices: Vec<crate::types::payment::LabeledPrice>,
) -> Self {
Self {
business_connection_id: None,
title: title.into(),
description: description.into(),
payload: payload.into(),
provider_token: None,
currency: currency.into(),
prices,
subscription_period: None,
max_tip_amount: None,
suggested_tip_amounts: None,
provider_data: None,
photo_url: None,
photo_size: None,
photo_width: None,
photo_height: None,
need_name: None,
need_phone_number: None,
need_email: None,
need_shipping_address: None,
send_phone_number_to_provider: None,
send_email_to_provider: None,
is_flexible: None,
}
}
}
impl AdvancedRequest for AdvancedCreateInvoiceLinkRequest {
type Response = String;
const METHOD: &'static str = "createInvoiceLink";
fn validate(&self) -> Result<()> {
if let Some(value) = self.business_connection_id.as_deref() {
validate_string_id("business_connection_id", value)?;
}
validate_required_string("title", &self.title)?;
validate_required_string("description", &self.description)?;
validate_required_string("payload", &self.payload)?;
validate_required_string("currency", &self.currency)?;
validate_required_items::<crate::types::payment::LabeledPrice>("prices", &self.prices)?;
if let Some(value) = self.subscription_period {
validate_positive_i64("subscription_period", value)?;
}
if let Some(value) = self.max_tip_amount {
validate_positive_i64("max_tip_amount", value)?;
}
if let Some(value) = self.photo_size {
validate_positive_i64("photo_size", value)?;
}
if let Some(value) = self.photo_width {
validate_positive_i64("photo_width", value)?;
}
if let Some(value) = self.photo_height {
validate_positive_i64("photo_height", value)?;
}
Ok(())
}
}
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedAnswerShippingQueryRequest {
pub shipping_query_id: String,
pub ok: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shipping_options: Option<Vec<crate::types::payment::ShippingOption>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
}
impl AdvancedAnswerShippingQueryRequest {
pub fn new(shipping_query_id: impl Into<String>, ok: bool) -> Self {
Self {
shipping_query_id: shipping_query_id.into(),
ok,
shipping_options: None,
error_message: None,
}
}
}
impl AdvancedRequest for AdvancedAnswerShippingQueryRequest {
type Response = Value;
const METHOD: &'static str = "answerShippingQuery";
fn validate(&self) -> Result<()> {
validate_string_id("shipping_query_id", &self.shipping_query_id)?;
if self.ok {
if self.error_message.is_some() {
return Err(Error::InvalidRequest {
reason: "answerShippingQuery must omit `error_message` when `ok` is true"
.to_owned(),
});
}
let Some(shipping_options) = self.shipping_options.as_deref() else {
return Err(Error::InvalidRequest {
reason: "answerShippingQuery requires `shipping_options` when `ok` is true"
.to_owned(),
});
};
validate_required_items::<crate::types::payment::ShippingOption>(
"shipping_options",
shipping_options,
)?;
} else {
if self.shipping_options.is_some() {
return Err(Error::InvalidRequest {
reason: "answerShippingQuery must omit `shipping_options` when `ok` is false"
.to_owned(),
});
}
if self
.error_message
.as_deref()
.is_none_or(|value| value.trim().is_empty())
{
return Err(Error::InvalidRequest {
reason:
"answerShippingQuery requires non-empty `error_message` when `ok` is false"
.to_owned(),
});
}
}
Ok(())
}
}
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedAnswerPreCheckoutQueryRequest {
pub pre_checkout_query_id: String,
pub ok: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_message: Option<String>,
}
impl AdvancedAnswerPreCheckoutQueryRequest {
pub fn new(pre_checkout_query_id: impl Into<String>, ok: bool) -> Self {
Self {
pre_checkout_query_id: pre_checkout_query_id.into(),
ok,
error_message: None,
}
}
}
impl AdvancedRequest for AdvancedAnswerPreCheckoutQueryRequest {
type Response = Value;
const METHOD: &'static str = "answerPreCheckoutQuery";
fn validate(&self) -> Result<()> {
validate_string_id("pre_checkout_query_id", &self.pre_checkout_query_id)?;
if self.ok {
if self.error_message.is_some() {
return Err(Error::InvalidRequest {
reason: "answerPreCheckoutQuery must omit `error_message` when `ok` is true"
.to_owned(),
});
}
} else if self
.error_message
.as_deref()
.is_none_or(|value| value.trim().is_empty())
{
return Err(Error::InvalidRequest {
reason:
"answerPreCheckoutQuery requires non-empty `error_message` when `ok` is false"
.to_owned(),
});
}
Ok(())
}
}
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedSetPassportDataErrorsRequest {
pub user_id: crate::types::common::UserId,
pub errors: Vec<crate::types::telegram::PassportElementError>,
}
impl AdvancedSetPassportDataErrorsRequest {
pub fn new(
user_id: crate::types::common::UserId,
errors: Vec<crate::types::telegram::PassportElementError>,
) -> Self {
Self { user_id, errors }
}
}
impl AdvancedRequest for AdvancedSetPassportDataErrorsRequest {
type Response = bool;
const METHOD: &'static str = "setPassportDataErrors";
fn validate(&self) -> Result<()> {
self.user_id.validate()?;
validate_required_items::<crate::types::telegram::PassportElementError>(
"errors",
&self.errors,
)?;
Ok(())
}
}