1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
//
// ferogram: async Telegram MTProto client in Rust
// https://github.com/ankit-chaubey/ferogram
//
// Licensed under either the MIT License or the Apache License 2.0.
// See the LICENSE-MIT or LICENSE-APACHE file in this repository:
// https://github.com/ankit-chaubey/ferogram
//
// Feel free to use, modify, and share this code.
// Please keep this notice when redistributing.
use ferogram_tl_types as tl;
/// Builder for composing outgoing messages.
///
/// ```rust,no_run
/// use ferogram::InputMessage;
///
/// // plain text
/// let msg = InputMessage::text("Hello!");
///
/// // markdown
/// let msg = InputMessage::markdown("**bold** and _italic_");
///
/// // HTML
/// let msg = InputMessage::html("<b>bold</b> and <i>italic</i>");
///
/// // with options
/// let msg = InputMessage::markdown("**Hello**")
/// .silent(true)
/// .reply_to(Some(42));
/// ```
#[derive(Clone, Default)]
pub struct InputMessage {
pub text: String,
pub reply_to: Option<i32>,
pub silent: bool,
pub background: bool,
pub clear_draft: bool,
pub no_webpage: bool,
/// Show media above the caption instead of below (Telegram ≥ 10.3).\
pub invert_media: bool,
/// Schedule to send when the user goes online (`schedule_date = 0x7FFFFFFE`).\
pub schedule_once_online: bool,
pub entities: Option<Vec<tl::enums::MessageEntity>>,
pub reply_markup: Option<tl::enums::ReplyMarkup>,
pub schedule_date: Option<i32>,
/// Attached media to send alongside the message.
/// Use [`InputMessage::copy_media`] to attach media copied from an existing message.
pub media: Option<tl::enums::InputMedia>,
}
/// Options for forwarding messages.
///
/// Used by [`Client::forward_messages_with`], [`Client::forward_messages`] and
/// [`IncomingMessage::forward_to_ex`]. All fields default to `false`/`None`.
#[derive(Default, Clone)]
pub struct ForwardOptions {
/// Send silently (no notification for recipient).
pub silent: bool,
/// Strip the original author attribution (`Forwarded from …`).
pub drop_author: bool,
/// Remove captions from forwarded media.
pub drop_media_captions: bool,
/// Prevent recipients from forwarding the message further.
pub noforwards: bool,
/// Reply to an existing message in the destination chat.
pub reply_to: Option<i32>,
/// Schedule forwarding for this Unix timestamp (seconds).
pub schedule_date: Option<i32>,
}
/// Selects which flavour of message link [`Client::export_message_link`] should produce.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LinkKind {
/// A plain `t.me/channel/msgid` permalink (default).
#[default]
Normal,
/// A link that reveals the whole album / media group the message belongs to.
Grouped,
/// A link that opens the thread (comments) attached to a channel post.
Thread,
}
impl InputMessage {
/// Create a message with the given text.
pub fn text(text: impl Into<String>) -> Self {
Self {
text: text.into(),
..Default::default()
}
}
/// Create a message by parsing Telegram-flavoured markdown.
///
/// The markdown is stripped and the resulting plain text + entities are
/// set on the message. Supports `**bold**`, `_italic_`, `` `code` ``,
/// `[text](url)`, `||spoiler||`, `~~strike~~`, ``,
/// and backslash escapes.
///
/// ```rust,no_run
/// use ferogram::InputMessage;
///
/// let msg = InputMessage::markdown("**Hello** _world_!");
/// ```
pub fn markdown(text: impl AsRef<str>) -> Self {
let (plain, ents) = crate::parsers::parse_markdown(text.as_ref());
Self {
text: plain,
entities: if ents.is_empty() { None } else { Some(ents) },
..Default::default()
}
}
/// Create a message by parsing Telegram-compatible HTML.
///
/// Supports `<b>`, `<i>`, `<u>`, `<s>`, `<code>`, `<pre>`,
/// `<tg-spoiler>`, `<a href="...">`, `<tg-emoji emoji-id="...">`.
///
/// ```rust,no_run
/// use ferogram::InputMessage;
///
/// let msg = InputMessage::html("<b>Hello</b> <i>world</i>!");
/// ```
pub fn html(text: impl AsRef<str>) -> Self {
let (plain, ents) = crate::parsers::parse_html(text.as_ref());
Self {
text: plain,
entities: if ents.is_empty() { None } else { Some(ents) },
..Default::default()
}
}
/// Set the message text.
pub fn set_text(mut self, text: impl Into<String>) -> Self {
self.text = text.into();
self
}
/// Reply to a specific message ID.
pub fn reply_to(mut self, id: Option<i32>) -> Self {
self.reply_to = id;
self
}
/// Send silently (no notification sound).
pub fn silent(mut self, v: bool) -> Self {
self.silent = v;
self
}
/// Send in background.
pub fn background(mut self, v: bool) -> Self {
self.background = v;
self
}
/// Clear the draft after sending.
pub fn clear_draft(mut self, v: bool) -> Self {
self.clear_draft = v;
self
}
/// Disable link preview.
pub fn no_webpage(mut self, v: bool) -> Self {
self.no_webpage = v;
self
}
/// Show media above the caption rather than below (requires Telegram ≥ 10.3).
pub fn invert_media(mut self, v: bool) -> Self {
self.invert_media = v;
self
}
/// Schedule the message to be sent when the recipient comes online.
///
/// Mutually exclusive with `schedule_date`: calling this last wins.
/// Uses the Telegram magic value `0x7FFFFFFE`.
pub fn schedule_once_online(mut self) -> Self {
self.schedule_once_online = true;
self.schedule_date = None;
self
}
/// Attach formatting entities (bold, italic, code, links, etc).
pub fn entities(mut self, e: Vec<tl::enums::MessageEntity>) -> Self {
self.entities = Some(e);
self
}
/// Attach a reply markup (inline or reply keyboard).
pub fn reply_markup(mut self, rm: impl Into<tl::enums::ReplyMarkup>) -> Self {
self.reply_markup = Some(rm.into());
self
}
/// Schedule the message for a future Unix timestamp.
pub fn schedule_date(mut self, ts: Option<i32>) -> Self {
self.schedule_date = ts;
self
}
/// Attach media copied from an existing message.
///
/// Pass the `InputMedia` obtained from [`crate::media::Photo`],
/// [`crate::media::Document`], or directly from a raw `MessageMedia`.
///
/// When a `media` is set, the message is sent via `messages.SendMedia`
/// instead of `messages.SendMessage`.
///
/// ```rust,no_run
/// # use ferogram::{InputMessage, tl};
/// # fn example(media: tl::enums::InputMedia) {
/// let msg = InputMessage::text("Here is the file again")
/// .copy_media(media);
/// # }
/// ```
pub fn copy_media(mut self, media: tl::enums::InputMedia) -> Self {
self.media = Some(media);
self
}
/// Remove any previously attached media.
pub fn clear_media(mut self) -> Self {
self.media = None;
self
}
pub(crate) fn reply_header(&self) -> Option<tl::enums::InputReplyTo> {
self.reply_to.map(|id| {
tl::enums::InputReplyTo::Message(tl::types::InputReplyToMessage {
reply_to_msg_id: id,
top_msg_id: None,
reply_to_peer_id: None,
quote_text: None,
quote_entities: None,
quote_offset: None,
monoforum_peer_id: None,
todo_item_id: None,
poll_option: None,
})
})
}
}
impl From<&str> for InputMessage {
fn from(s: &str) -> Self {
Self::text(s)
}
}
impl From<String> for InputMessage {
fn from(s: String) -> Self {
Self::text(s)
}
}
/// Groups all invoice parameters for [`crate::Client::send_invoice`].
#[derive(Debug, Default, Clone)]
pub struct InvoiceOptions {
/// Three-letter ISO 4217 currency code (e.g. `"USD"`).
pub currency: String,
/// Line items: `(label, amount_in_smallest_units)`.
pub prices: Vec<(String, i64)>,
/// Optional URL of a photo to attach to the invoice.
pub photo_url: Option<String>,
/// Request the payer's full name.
pub need_name: bool,
/// Request the payer's phone number.
pub need_phone: bool,
/// Request the payer's email address.
pub need_email: bool,
/// Request the payer's shipping address.
pub need_shipping_address: bool,
/// Whether the final price depends on the shipping method.
pub is_flexible: bool,
}