Skip to main content

layer_client/
search.rs

1//! Fluent search builders.
2//!
3//! # In-chat search
4//! ```rust,no_run
5//! # async fn f(client: layer_client::Client, peer: layer_tl_types::enums::Peer)
6//! # -> Result<(), Box<dyn std::error::Error>> {
7//! let results = client
8//! .search(peer, "hello world")
9//! .min_date(1_700_000_000)
10//! .max_date(1_720_000_000)
11//! .filter(layer_tl_types::enums::MessagesFilter::InputMessagesFilterPhotos)
12//! .limit(50)
13//! .fetch(&client)
14//! .await?;
15//! # Ok(()) }
16//! ```
17//!
18//! # Global search
19//! ```rust,no_run
20//! # async fn f(client: layer_client::Client)
21//! # -> Result<(), Box<dyn std::error::Error>> {
22//! let results = client
23//! .search_global_builder("rust async")
24//! .broadcasts_only(true)
25//! .min_date(1_700_000_000)
26//! .limit(30)
27//! .fetch(&client)
28//! .await?;
29//! # Ok(()) }
30//! ```
31
32use layer_tl_types::{self as tl, Cursor, Deserializable};
33
34use crate::{Client, InvocationError, PeerRef, update};
35
36/// Fluent builder for `messages.search` (in-chat message search).
37///
38/// Created by [`Client::search`]. All setters are chainable; call
39/// [`fetch`] to execute.
40///
41/// [`fetch`]: SearchBuilder::fetch
42pub struct SearchBuilder {
43    peer: PeerRef,
44    query: String,
45    filter: tl::enums::MessagesFilter,
46    min_date: i32,
47    max_date: i32,
48    offset_id: i32,
49    add_offset: i32,
50    limit: i32,
51    max_id: i32,
52    min_id: i32,
53    from_id: Option<tl::enums::InputPeer>,
54    top_msg_id: Option<i32>,
55}
56
57impl SearchBuilder {
58    pub(crate) fn new(peer: PeerRef, query: String) -> Self {
59        Self {
60            peer,
61            query,
62            filter: tl::enums::MessagesFilter::InputMessagesFilterEmpty,
63            min_date: 0,
64            max_date: 0,
65            offset_id: 0,
66            add_offset: 0,
67            limit: 100,
68            max_id: 0,
69            min_id: 0,
70            from_id: None,
71            top_msg_id: None,
72        }
73    }
74
75    /// Only return messages on or after this Unix timestamp.
76    pub fn min_date(mut self, ts: i32) -> Self {
77        self.min_date = ts;
78        self
79    }
80
81    /// Only return messages on or before this Unix timestamp.
82    pub fn max_date(mut self, ts: i32) -> Self {
83        self.max_date = ts;
84        self
85    }
86
87    /// Apply a `MessagesFilter` (e.g. photos only, video only, etc.).
88    pub fn filter(mut self, f: tl::enums::MessagesFilter) -> Self {
89        self.filter = f;
90        self
91    }
92
93    /// Maximum number of messages to return (default 100).
94    pub fn limit(mut self, n: i32) -> Self {
95        self.limit = n;
96        self
97    }
98
99    /// Start from this message ID (for pagination).
100    pub fn offset_id(mut self, id: i32) -> Self {
101        self.offset_id = id;
102        self
103    }
104
105    /// Additional offset for fine-grained pagination.
106    pub fn add_offset(mut self, off: i32) -> Self {
107        self.add_offset = off;
108        self
109    }
110
111    /// Only return messages with an ID ≤ `max_id`.
112    pub fn max_id(mut self, id: i32) -> Self {
113        self.max_id = id;
114        self
115    }
116
117    /// Only return messages with an ID ≥ `min_id`.
118    pub fn min_id(mut self, id: i32) -> Self {
119        self.min_id = id;
120        self
121    }
122
123    /// Restrict to messages sent by this peer (resolved against the cache).
124    /// Only return messages sent by the logged-in user.
125    pub fn sent_by_self(mut self) -> Self {
126        self.from_id = Some(tl::enums::InputPeer::PeerSelf);
127        self
128    }
129
130    /// Only return messages sent by a specific peer.
131    pub fn from_peer(mut self, peer: tl::enums::InputPeer) -> Self {
132        self.from_id = Some(peer);
133        self
134    }
135
136    /// Restrict search to a specific forum topic.
137    pub fn top_msg_id(mut self, id: i32) -> Self {
138        self.top_msg_id = Some(id);
139        self
140    }
141
142    /// Execute the search and return matching messages.
143    pub async fn fetch(
144        self,
145        client: &Client,
146    ) -> Result<Vec<update::IncomingMessage>, InvocationError> {
147        let peer = self.peer.resolve(client).await?;
148        let input_peer = client.inner.peer_cache.read().await.peer_to_input(&peer);
149        let req = tl::functions::messages::Search {
150            peer: input_peer,
151            q: self.query,
152            from_id: self.from_id,
153            saved_peer_id: None,
154            saved_reaction: None,
155            top_msg_id: self.top_msg_id,
156            filter: self.filter,
157            min_date: self.min_date,
158            max_date: self.max_date,
159            offset_id: self.offset_id,
160            add_offset: self.add_offset,
161            limit: self.limit,
162            max_id: self.max_id,
163            min_id: self.min_id,
164            hash: 0,
165        };
166        let body = client.rpc_call_raw(&req).await?;
167        let mut cur = Cursor::from_slice(&body);
168        let msgs = match tl::enums::messages::Messages::deserialize(&mut cur)? {
169            tl::enums::messages::Messages::Messages(m) => m.messages,
170            tl::enums::messages::Messages::Slice(m) => m.messages,
171            tl::enums::messages::Messages::ChannelMessages(m) => m.messages,
172            tl::enums::messages::Messages::NotModified(_) => vec![],
173        };
174        Ok(msgs
175            .into_iter()
176            .map(update::IncomingMessage::from_raw)
177            .collect())
178    }
179}
180
181/// Fluent builder for `messages.searchGlobal` (cross-chat search).
182///
183/// Created by [`Client::search_global_builder`]. All setters are chainable;
184/// call [`fetch`] to execute.
185///
186/// [`fetch`]: GlobalSearchBuilder::fetch
187pub struct GlobalSearchBuilder {
188    query: String,
189    filter: tl::enums::MessagesFilter,
190    min_date: i32,
191    max_date: i32,
192    offset_rate: i32,
193    offset_id: i32,
194    limit: i32,
195    folder_id: Option<i32>,
196    broadcasts_only: bool,
197    groups_only: bool,
198    users_only: bool,
199}
200
201impl GlobalSearchBuilder {
202    pub(crate) fn new(query: String) -> Self {
203        Self {
204            query,
205            filter: tl::enums::MessagesFilter::InputMessagesFilterEmpty,
206            min_date: 0,
207            max_date: 0,
208            offset_rate: 0,
209            offset_id: 0,
210            limit: 100,
211            folder_id: None,
212            broadcasts_only: false,
213            groups_only: false,
214            users_only: false,
215        }
216    }
217
218    /// Restrict to a specific dialog folder.
219    pub fn folder_id(mut self, id: i32) -> Self {
220        self.folder_id = Some(id);
221        self
222    }
223
224    /// Only return results from broadcast channels.
225    pub fn broadcasts_only(mut self, v: bool) -> Self {
226        self.broadcasts_only = v;
227        self
228    }
229
230    /// Only return results from groups/supergroups.
231    pub fn groups_only(mut self, v: bool) -> Self {
232        self.groups_only = v;
233        self
234    }
235
236    /// Only return results from private chats / bots.
237    pub fn users_only(mut self, v: bool) -> Self {
238        self.users_only = v;
239        self
240    }
241
242    /// Apply a `MessagesFilter` (e.g. photos, video, etc.).
243    pub fn filter(mut self, f: tl::enums::MessagesFilter) -> Self {
244        self.filter = f;
245        self
246    }
247
248    /// Only return messages on or after this Unix timestamp.
249    pub fn min_date(mut self, ts: i32) -> Self {
250        self.min_date = ts;
251        self
252    }
253
254    /// Only return messages on or before this Unix timestamp.
255    pub fn max_date(mut self, ts: i32) -> Self {
256        self.max_date = ts;
257        self
258    }
259
260    /// Pagination: rate from the previous response's last message.
261    pub fn offset_rate(mut self, r: i32) -> Self {
262        self.offset_rate = r;
263        self
264    }
265
266    /// Pagination: start from this message ID.
267    pub fn offset_id(mut self, id: i32) -> Self {
268        self.offset_id = id;
269        self
270    }
271
272    /// Maximum number of messages to return (default 100).
273    pub fn limit(mut self, n: i32) -> Self {
274        self.limit = n;
275        self
276    }
277
278    /// Execute the global search and return matching messages.
279    pub async fn fetch(
280        self,
281        client: &Client,
282    ) -> Result<Vec<update::IncomingMessage>, InvocationError> {
283        let req = tl::functions::messages::SearchGlobal {
284            broadcasts_only: self.broadcasts_only,
285            groups_only: self.groups_only,
286            users_only: self.users_only,
287            folder_id: self.folder_id,
288            q: self.query,
289            filter: self.filter,
290            min_date: self.min_date,
291            max_date: self.max_date,
292            offset_rate: self.offset_rate,
293            offset_peer: tl::enums::InputPeer::Empty,
294            offset_id: self.offset_id,
295            limit: self.limit,
296        };
297        let body = client.rpc_call_raw(&req).await?;
298        let mut cur = Cursor::from_slice(&body);
299        let msgs = match tl::enums::messages::Messages::deserialize(&mut cur)? {
300            tl::enums::messages::Messages::Messages(m) => m.messages,
301            tl::enums::messages::Messages::Slice(m) => m.messages,
302            tl::enums::messages::Messages::ChannelMessages(m) => m.messages,
303            tl::enums::messages::Messages::NotModified(_) => vec![],
304        };
305        Ok(msgs
306            .into_iter()
307            .map(update::IncomingMessage::from_raw)
308            .collect())
309    }
310}