egg_mode/user/fun.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use crate::common::*;
6use crate::error::Result;
7use crate::{auth, cursor, links};
8
9use super::*;
10
11//---Groups of users---
12
13/// Look up profile information for several Twitter users.
14///
15/// This function is set up so it can be called with a few different Item types; whether just IDs
16/// with `u64`, just screen names with `&str` or `String`, or even a mix of both (by using `UserID`
17/// directly).
18///
19/// ## Examples
20///
21/// ```rust,no_run
22/// # use egg_mode::Token;
23/// # #[tokio::main]
24/// # async fn main() {
25/// # let token: Token = unimplemented!();
26/// let mut list: Vec<u64> = Vec::new();
27///
28/// list.push(1234);
29/// list.push(2345);
30///
31/// let users = egg_mode::user::lookup(list, &token).await.unwrap();
32/// # }
33/// ```
34///
35/// ```rust,no_run
36/// # use egg_mode::Token;
37/// # #[tokio::main]
38/// # async fn main() {
39/// # let token: Token = unimplemented!();
40/// let mut list: Vec<&str> = Vec::new();
41///
42/// list.push("rustlang");
43/// list.push("ThisWeekInRust");
44///
45/// let users = egg_mode::user::lookup(list, &token).await.unwrap();
46/// # }
47/// ```
48///
49/// ```rust,no_run
50/// # use egg_mode::Token;
51/// # #[tokio::main]
52/// # async fn main() {
53/// # let token: Token = unimplemented!();
54/// let mut list: Vec<String> = Vec::new();
55///
56/// list.push("rustlang".to_string());
57/// list.push("ThisWeekInRust".to_string());
58///
59/// let users = egg_mode::user::lookup(list, &token).await.unwrap();
60/// # }
61/// ```
62///
63/// ```rust,no_run
64/// # use egg_mode::Token;
65/// # #[tokio::main]
66/// # async fn main() {
67/// # let token: Token = unimplemented!();
68/// let mut list: Vec<egg_mode::user::UserID> = Vec::new();
69///
70/// list.push(1234.into());
71/// list.push("rustlang".into());
72///
73/// let users = egg_mode::user::lookup(list, &token).await.unwrap();
74/// # }
75/// ```
76pub async fn lookup<T, I>(accts: I, token: &auth::Token) -> Result<Response<Vec<TwitterUser>>>
77where
78 T: Into<UserID>,
79 I: IntoIterator<Item = T>,
80{
81 let (id_param, name_param) = multiple_names_param(accts);
82
83 let params = ParamList::new()
84 .extended_tweets()
85 .add_param("user_id", id_param)
86 .add_param("screen_name", name_param);
87
88 let req = post(links::users::LOOKUP, token, Some(¶ms));
89
90 request_with_json_response(req).await
91}
92
93/// Lookup user information for a single user.
94pub async fn show<T: Into<UserID>>(acct: T, token: &auth::Token) -> Result<Response<TwitterUser>> {
95 let params = ParamList::new()
96 .extended_tweets()
97 .add_user_param(acct.into());
98
99 let req = get(links::users::SHOW, token, Some(¶ms));
100
101 request_with_json_response(req).await
102}
103
104/// Lookup the user IDs that the authenticating user has disabled retweets from.
105///
106/// Use `update_follow` to enable/disable viewing retweets from a specific user.
107pub async fn friends_no_retweets(token: &auth::Token) -> Result<Response<Vec<u64>>> {
108 let req = get(links::users::FRIENDS_NO_RETWEETS, token, None);
109
110 request_with_json_response(req).await
111}
112
113/// Lookup relationship settings between two arbitrary users.
114pub async fn relation<F, T>(from: F, to: T, token: &auth::Token) -> Result<Response<Relationship>>
115where
116 F: Into<UserID>,
117 T: Into<UserID>,
118{
119 let mut params = match from.into() {
120 UserID::ID(id) => ParamList::new().add_param("source_id", id.to_string()),
121 UserID::ScreenName(name) => ParamList::new().add_param("source_screen_name", name),
122 };
123 match to.into() {
124 UserID::ID(id) => params.add_param_ref("target_id", id.to_string()),
125 UserID::ScreenName(name) => params.add_param_ref("target_screen_name", name),
126 };
127
128 let req = get(links::users::FRIENDSHIP_SHOW, token, Some(¶ms));
129
130 // the relationship returned by Twitter is actually contained within a `"relationship"` field,
131 // so this wrapper struct makes sure the data is loaded correctly
132 #[derive(Deserialize)]
133 struct RelationWrapper {
134 relationship: Relationship,
135 }
136
137 let resp: Response<RelationWrapper> = request_with_json_response(req).await?;
138
139 Ok(Response::map(resp, |r| r.relationship))
140}
141
142/// Lookup the relations between the authenticated user and the given accounts.
143pub async fn relation_lookup<T, I>(
144 accts: I,
145 token: &auth::Token,
146) -> Result<Response<Vec<RelationLookup>>>
147where
148 T: Into<UserID>,
149 I: IntoIterator<Item = T>,
150{
151 let (id_param, name_param) = multiple_names_param(accts);
152
153 let params = ParamList::new()
154 .add_param("user_id", id_param)
155 .add_param("screen_name", name_param);
156
157 let req = get(links::users::FRIENDSHIP_LOOKUP, token, Some(¶ms));
158
159 request_with_json_response(req).await
160}
161
162//---Cursored collections---
163
164/// Lookup users based on the given search term.
165///
166/// This function returns a stream over the `TwitterUser` objects returned by Twitter. Due to a
167/// limitation in the API, you can only obtain the first 1000 search results. This method defaults
168/// to returning 10 users in a single network call; the maximum is 20. See the [`UserSearch`][]
169/// page for details.
170///
171/// [`UserSearch`]: struct.UserSearch.html
172pub fn search<S: Into<CowStr>>(query: S, token: &auth::Token) -> UserSearch {
173 UserSearch::new(query, token)
174}
175
176/// Lookup the users a given account follows, also called their "friends" within the API.
177///
178/// This function returns a stream over the `TwitterUser` objects returned by Twitter. This
179/// method defaults to returning 20 users in a single network call; the maximum is 200.
180pub fn friends_of<T: Into<UserID>>(
181 acct: T,
182 token: &auth::Token,
183) -> cursor::CursorIter<cursor::UserCursor> {
184 let params = ParamList::new().add_user_param(acct.into());
185 cursor::CursorIter::new(links::users::FRIENDS_LIST, token, Some(params), Some(20))
186}
187
188/// Lookup the users a given account follows, also called their "friends" within the API, but only
189/// return their user IDs.
190///
191/// This function returns a stream over the User IDs returned by Twitter. This method defaults to
192/// returning 500 IDs in a single network call; the maximum is 5000.
193///
194/// Choosing only to load the user IDs instead of the full user information results in a call that
195/// can return more accounts per-page, which can be useful if you anticipate having to page through
196/// several results and don't need all the user information.
197pub fn friends_ids<T: Into<UserID>>(
198 acct: T,
199 token: &auth::Token,
200) -> cursor::CursorIter<cursor::IDCursor> {
201 let params = ParamList::new().add_user_param(acct.into());
202 cursor::CursorIter::new(links::users::FRIENDS_IDS, token, Some(params), Some(500))
203}
204
205/// Lookup the users that follow a given account.
206///
207/// This function returns a stream over the `TwitterUser` objects returned by Twitter. This
208/// method defaults to returning 20 users in a single network call; the maximum is 200.
209pub fn followers_of<T: Into<UserID>>(
210 acct: T,
211 token: &auth::Token,
212) -> cursor::CursorIter<cursor::UserCursor> {
213 let params = ParamList::new()
214 .extended_tweets()
215 .add_user_param(acct.into());
216 cursor::CursorIter::new(links::users::FOLLOWERS_LIST, token, Some(params), Some(20))
217}
218
219/// Lookup the users that follow a given account, but only return their user IDs.
220///
221/// This function returns a stream over the User IDs returned by Twitter. This method defaults to
222/// returning 500 IDs in a single network call; the maximum is 5000.
223///
224/// Choosing only to load the user IDs instead of the full user information results in a call that
225/// can return more accounts per-page, which can be useful if you anticipate having to page through
226/// several results and don't need all the user information.
227pub fn followers_ids<T: Into<UserID>>(
228 acct: T,
229 token: &auth::Token,
230) -> cursor::CursorIter<cursor::IDCursor> {
231 let params = ParamList::new().add_user_param(acct.into());
232 cursor::CursorIter::new(links::users::FOLLOWERS_IDS, token, Some(params), Some(500))
233}
234
235/// Lookup the users that have been blocked by the authenticated user.
236///
237/// Note that while loading a user's blocks list is a cursored search, it does not allow you to set
238/// the page size. Calling `with_page_size` on a stream returned by this function will not
239/// change the page size used by the network call. Setting `page_size` manually may result in an
240/// error from Twitter.
241pub fn blocks(token: &auth::Token) -> cursor::CursorIter<cursor::UserCursor> {
242 cursor::CursorIter::new(links::users::BLOCKS_LIST, token, None, None)
243}
244
245/// Lookup the users that have been blocked by the authenticated user, but only return their user
246/// IDs.
247///
248/// Choosing only to load the user IDs instead of the full user information results in a call that
249/// can return more accounts per-page, which can be useful if you anticipate having to page through
250/// several results and don't need all the user information.
251///
252/// Note that while loading a user's blocks list is a cursored search, it does not allow you to set
253/// the page size. Calling `with_page_size` on a stream returned by this function will not
254/// change the page size used by the network call. Setting `page_size` manually may result in an
255/// error from Twitter.
256pub fn blocks_ids(token: &auth::Token) -> cursor::CursorIter<cursor::IDCursor> {
257 cursor::CursorIter::new(links::users::BLOCKS_IDS, token, None, None)
258}
259
260/// Lookup the users that have been muted by the authenticated user.
261///
262/// Note that while loading a user's mutes list is a cursored search, it does not allow you to set
263/// the page size. Calling `with_page_size` on a stream returned by this function will not
264/// change the page size used by the network call. Setting `page_size` manually may result in an
265/// error from Twitter.
266pub fn mutes(token: &auth::Token) -> cursor::CursorIter<cursor::UserCursor> {
267 cursor::CursorIter::new(links::users::MUTES_LIST, token, None, None)
268}
269
270/// Lookup the users that have been muted by the authenticated user, but only return their user IDs.
271///
272/// Choosing only to load the user IDs instead of the full user information results in a call that
273/// can return more accounts per-page, which can be useful if you anticipate having to page through
274/// several results and don't need all the user information.
275///
276/// Note that while loading a user's mutes list is a cursored search, it does not allow you to set
277/// the page size. Calling `with_page_size` on a stream returned by this function will not
278/// change the page size used by the network call. Setting `page_size` manually may result in an
279/// error from Twitter.
280pub fn mutes_ids(token: &auth::Token) -> cursor::CursorIter<cursor::IDCursor> {
281 cursor::CursorIter::new(links::users::MUTES_IDS, token, None, None)
282}
283
284/// Lookup the user IDs who have pending requests to follow the authenticated protected user.
285///
286/// If the authenticated user is not a protected account, this will return an empty collection.
287pub fn incoming_requests(token: &auth::Token) -> cursor::CursorIter<cursor::IDCursor> {
288 cursor::CursorIter::new(links::users::FRIENDSHIPS_INCOMING, token, None, None)
289}
290
291/// Lookup the user IDs with which the authenticating user has a pending follow request.
292pub fn outgoing_requests(token: &auth::Token) -> cursor::CursorIter<cursor::IDCursor> {
293 cursor::CursorIter::new(links::users::FRIENDSHIPS_OUTGOING, token, None, None)
294}
295
296//---User actions---
297
298/// Follow the given account with the authenticated user, and set whether device notifications
299/// should be enabled.
300///
301/// Upon success, the future returned by this function yields the user that was just followed, even
302/// when following a protected account. In the latter case, this indicates that the follow request
303/// was successfully sent.
304///
305/// Calling this with an account the user already follows may return an error, or ("for performance
306/// reasons") may return success without changing any account settings.
307pub async fn follow<T: Into<UserID>>(
308 acct: T,
309 notifications: bool,
310 token: &auth::Token,
311) -> Result<Response<TwitterUser>> {
312 let params = ParamList::new()
313 .extended_tweets()
314 .add_user_param(acct.into())
315 .add_param("follow", notifications.to_string());
316 let req = post(links::users::FOLLOW, token, Some(¶ms));
317 request_with_json_response(req).await
318}
319
320/// Unfollow the given account with the authenticated user.
321///
322/// Upon success, the future returned by this function yields the user that was just unfollowed.
323///
324/// Calling this with an account the user doesn't follow will return success, even though it doesn't
325/// change any settings.
326pub async fn unfollow<T: Into<UserID>>(
327 acct: T,
328 token: &auth::Token,
329) -> Result<Response<TwitterUser>> {
330 let params = ParamList::new()
331 .extended_tweets()
332 .add_user_param(acct.into());
333 let req = post(links::users::UNFOLLOW, token, Some(¶ms));
334 request_with_json_response(req).await
335}
336
337/// Update notification settings and reweet visibility for the given user.
338///
339/// Calling this for an account the authenticated user does not already follow will not cause them
340/// to follow that user. It will return an error if you pass `Some(true)` for `notifications` or
341/// `Some(false)` for `retweets`. Any other combination of arguments will return a `Relationship` as
342/// if you had called `relation` between the authenticated user and the given user.
343pub async fn update_follow<T>(
344 acct: T,
345 notifications: Option<bool>,
346 retweets: Option<bool>,
347 token: &auth::Token,
348) -> Result<Response<Relationship>>
349where
350 T: Into<UserID>,
351{
352 let params = ParamList::new()
353 .add_user_param(acct.into())
354 .add_opt_param("device", notifications.map(|v| v.to_string()))
355 .add_opt_param("retweets", retweets.map(|v| v.to_string()));
356 let req = post(links::users::FRIENDSHIP_UPDATE, token, Some(¶ms));
357 request_with_json_response(req).await
358}
359
360/// Block the given account with the authenticated user.
361///
362/// Upon success, the future returned by this function yields the given user.
363pub async fn block<T: Into<UserID>>(acct: T, token: &auth::Token) -> Result<Response<TwitterUser>> {
364 let params = ParamList::new()
365 .extended_tweets()
366 .add_user_param(acct.into());
367 let req = post(links::users::BLOCK, token, Some(¶ms));
368 request_with_json_response(req).await
369}
370
371/// Block the given account and report it for spam, with the authenticated user.
372///
373/// Upon success, the future returned by this function yields the given user.
374pub async fn report_spam<T: Into<UserID>>(
375 acct: T,
376 token: &auth::Token,
377) -> Result<Response<TwitterUser>> {
378 let params = ParamList::new()
379 .extended_tweets()
380 .add_user_param(acct.into());
381 let req = post(links::users::REPORT_SPAM, token, Some(¶ms));
382 request_with_json_response(req).await
383}
384
385/// Unblock the given user with the authenticated user.
386///
387/// Upon success, the future returned by this function yields the given user.
388pub async fn unblock<T: Into<UserID>>(
389 acct: T,
390 token: &auth::Token,
391) -> Result<Response<TwitterUser>> {
392 let params = ParamList::new()
393 .extended_tweets()
394 .add_user_param(acct.into());
395 let req = post(links::users::UNBLOCK, token, Some(¶ms));
396 request_with_json_response(req).await
397}
398
399/// Mute the given user with the authenticated user.
400///
401/// Upon success, the future returned by this function yields the given user.
402pub async fn mute<T: Into<UserID>>(acct: T, token: &auth::Token) -> Result<Response<TwitterUser>> {
403 let params = ParamList::new()
404 .extended_tweets()
405 .add_user_param(acct.into());
406 let req = post(links::users::MUTE, token, Some(¶ms));
407 request_with_json_response(req).await
408}
409
410/// Unmute the given user with the authenticated user.
411///
412/// Upon success, the future returned by this function yields the given user.
413pub async fn unmute<T: Into<UserID>>(
414 acct: T,
415 token: &auth::Token,
416) -> Result<Response<TwitterUser>> {
417 let params = ParamList::new()
418 .extended_tweets()
419 .add_user_param(acct.into());
420 let req = post(links::users::UNMUTE, token, Some(¶ms));
421 request_with_json_response(req).await
422}