misskey_util/client.rs
1use std::path::Path;
2
3#[cfg(feature = "12-9-0")]
4use crate::builder::EmojiUpdateBuilder;
5#[cfg(feature = "12-27-0")]
6use crate::builder::NotificationBuilder;
7use crate::builder::{
8 AnnouncementUpdateBuilder, AntennaBuilder, AntennaUpdateBuilder, DriveFileBuilder,
9 DriveFileListBuilder, DriveFileUpdateBuilder, DriveFileUrlBuilder, DriveFolderUpdateBuilder,
10 MeUpdateBuilder, MessagingMessageBuilder, MetaUpdateBuilder, NoteBuilder, ServerLogListBuilder,
11 UserListBuilder,
12};
13#[cfg(feature = "12-47-0")]
14use crate::builder::{ChannelBuilder, ChannelUpdateBuilder};
15#[cfg(feature = "12-57-0")]
16use crate::builder::{ClipBuilder, ClipUpdateBuilder};
17use crate::pager::{BackwardPager, BoxPager, ForwardPager, OffsetPager, PagerStream};
18use crate::Error;
19use crate::{TimelineCursor, TimelineRange};
20
21#[cfg(feature = "12-13-0")]
22use chrono::DateTime;
23use chrono::Utc;
24use futures::{future::BoxFuture, stream::TryStreamExt};
25use mime::Mime;
26#[cfg(feature = "12-47-0")]
27use misskey_api::model::channel::Channel;
28#[cfg(feature = "12-58-0")]
29use misskey_api::model::page::Page;
30use misskey_api::model::{
31 abuse_user_report::AbuseUserReport,
32 announcement::Announcement,
33 antenna::Antenna,
34 clip::Clip,
35 drive::{DriveFile, DriveFolder},
36 emoji::Emoji,
37 following::FollowRequest,
38 id::Id,
39 log::ModerationLog,
40 messaging::MessagingMessage,
41 meta::Meta,
42 note::{Note, Reaction, Tag},
43 notification::Notification,
44 query::Query,
45 user::{User, UserRelation},
46 user_group::{UserGroup, UserGroupInvitation},
47 user_list::UserList,
48};
49use misskey_api::{endpoint, EntityRef};
50use misskey_core::{Client, UploadFileClient};
51use url::Url;
52
53// {{{ Utility
54macro_rules! impl_timeline_method {
55 ($timeline:ident, $endpoint:path $(,$reqname:ident = $argname:ident : $argentity:ident)* ) => {
56 paste::paste! {
57 #[doc = "Lists the notes in the specified range of the " $timeline " timeline."]
58 ///
59 /// The bound `Into<TimelineRange<Note>>` on the argument type is satisfied by the type
60 /// of some range expressions such as `..` or `start..` (which are desugared into [`RangeFull`][range_full] and
61 /// [`RangeFrom`][range_from] respectively). A note or [`DateTime<Utc>`][datetime] can
62 /// be used to specify the start and end bounds of the range.
63 ///
64 /// [range_full]: std::ops::RangeFull
65 /// [range_from]: std::ops::RangeFrom
66 /// [datetime]: chrono::DateTime
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// # use misskey_util::ClientExt;
72 /// # use futures::stream::TryStreamExt;
73 /// # #[tokio::main]
74 /// # async fn main() -> anyhow::Result<()> {
75 /// # let client = misskey_test::test_client().await?;
76 /// # let user = client.users().list().try_next().await?.unwrap();
77 /// # #[cfg(feature = "12-47-0")]
78 /// # let channel = client.create_channel("test").await?;
79 /// # let list = client.create_user_list("test").await?;
80 /// use futures::stream::{StreamExt, TryStreamExt};
81 ///
82 #[doc = "// `notes` variable here is a `Stream` to enumerate first 100 " $timeline " notes."]
83 #[doc = "let mut notes = client." $timeline "_notes(" $("&" $argname ", ")* "..).take(100);"]
84 ///
85 /// // Retrieve all notes until there are no more.
86 /// while let Some(note) = notes.try_next().await? {
87 /// // Print the text of the note, if any.
88 /// if let Some(text) = note.text {
89 /// println!("@{}: {}", note.user.username, text);
90 /// }
91 /// }
92 /// # Ok(())
93 /// # }
94 /// ```
95 ///
96 /// ```
97 /// # use misskey_util::ClientExt;
98 /// # use futures::stream::TryStreamExt;
99 /// # #[tokio::main]
100 /// # async fn main() -> anyhow::Result<()> {
101 /// # let client = misskey_test::test_client().await?;
102 /// # let user = client.users().list().try_next().await?.unwrap();
103 /// # #[cfg(feature = "12-47-0")]
104 /// # let channel = client.create_channel("test").await?;
105 /// # let list = client.create_user_list("test").await?;
106 /// use chrono::Utc;
107 ///
108 #[doc = "// Get the " $timeline " notes since `time`."]
109 /// let time = Utc::today().and_hms(0, 0, 0);
110 #[doc = "let mut notes = client." $timeline "_notes(" $("&" $argname ", ")* "time..);"]
111 /// # Ok(())
112 /// # }
113 /// ```
114 fn [<$timeline _notes>] (
115 &self,
116 $($argname : impl EntityRef<$argentity>,)*
117 range: impl Into<TimelineRange<Note>>,
118 ) -> PagerStream<BoxPager<Self, Note>> {
119 $(
120 let $reqname = $argname.entity_ref();
121 )*
122 let base_request =
123 endpoint::$endpoint::Request::builder()
124 $(.$reqname($reqname))*
125 .build();
126 let pager = match range.into() {
127 TimelineRange::Id {
128 since_id,
129 until_id: None,
130 } => BackwardPager::with_since_id(
131 self,
132 since_id,
133 base_request
134 ),
135 TimelineRange::Id {
136 since_id,
137 until_id: Some(until_id),
138 } => BackwardPager::new(
139 self,
140 endpoint::$endpoint::Request {
141 since_id,
142 until_id: Some(until_id),
143 ..base_request
144 },
145 ),
146 TimelineRange::DateTime {
147 since_date,
148 until_date,
149 } => BackwardPager::new(
150 self,
151 endpoint::$endpoint::Request {
152 since_date,
153 until_date: Some(until_date.unwrap_or(Utc::now())),
154 ..base_request
155 },
156 ),
157 TimelineRange::Unbounded => {
158 BackwardPager::new(self, base_request)
159 }
160 };
161 PagerStream::new(Box::pin(pager))
162 }
163
164 #[doc = "Lists all notes since the specified point in the " $timeline " timeline in reverse order (i.e. the old note comes first, the new note comes after)."]
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// # use misskey_util::ClientExt;
170 /// # use futures::stream::TryStreamExt;
171 /// # #[tokio::main]
172 /// # async fn main() -> anyhow::Result<()> {
173 /// # let client = misskey_test::test_client().await?;
174 /// # let user = client.users().list().try_next().await?.unwrap();
175 /// # #[cfg(feature = "12-47-0")]
176 /// # let channel = client.create_channel("test").await?;
177 /// # let list = client.create_user_list("test").await?;
178 /// use futures::stream::{StreamExt, TryStreamExt};
179 /// use chrono::Utc;
180 ///
181 /// let time = Utc::today().and_hms(0, 0, 0);
182 ///
183 #[doc = "// `notes_since` is a `Stream` to enumerate first 100 " $timeline " notes since `time` in reverse order."]
184 #[doc = "let mut notes_since = client." $timeline "_notes_since(" $("&" $argname ", ")* "time).take(100);"]
185 ///
186 /// // Retrieve all notes until there are no more.
187 /// while let Some(note) = notes_since.try_next().await? {
188 /// // Print the text of the note, if any.
189 /// if let Some(text) = note.text {
190 /// println!("@{}: {}", note.user.username, text);
191 /// }
192 /// }
193 /// # Ok(())
194 /// # }
195 /// ```
196 fn [<$timeline _notes_since>] (
197 &self,
198 $($argname : impl EntityRef<$argentity>,)*
199 since: impl Into<TimelineCursor<Note>>,
200 ) -> PagerStream<BoxPager<Self, Note>> {
201 $(
202 let $reqname = $argname.entity_ref();
203 )*
204 let base_request =
205 endpoint::$endpoint::Request::builder()
206 $(.$reqname($reqname))*
207 .build();
208 let request = match since.into() {
209 TimelineCursor::DateTime(since_date) => endpoint::$endpoint::Request {
210 since_date: Some(since_date),
211 ..base_request
212 },
213 TimelineCursor::Id(since_id) => endpoint::$endpoint::Request {
214 since_id: Some(since_id),
215 ..base_request
216 },
217 };
218 let pager = ForwardPager::new(self, request);
219 PagerStream::new(Box::pin(pager))
220 }
221
222 #[doc = "Returns a set of streams that fetch notes around the specified point in the " $timeline " timeline in both directions."]
223 fn [<$timeline _notes_around>](
224 &self,
225 $($argname : impl EntityRef<$argentity>,)*
226 cursor: impl Into<TimelineCursor<Note>>,
227 ) -> (
228 PagerStream<BoxPager<Self, Note>>,
229 PagerStream<BoxPager<Self, Note>>,
230 ) {
231 let cursor = cursor.into();
232 $(
233 let $reqname = $argname.entity_ref();
234 )*
235 (
236 self.[<$timeline _notes_since>]($($reqname,)* cursor),
237 self.[<$timeline _notes>]($($reqname,)* TimelineRange::until(cursor)),
238 )
239 }
240 }
241 };
242}
243// }}}
244
245/// An extension trait for [`Client`][client] that provides convenient high-level APIs.
246///
247/// [client]: misskey_core::Client
248///
249/// # Streams
250///
251/// Some methods (e.g. [`followers`][followers], [`local_notes`][local_notes], etc.) return a [`Stream`][stream]
252/// that uses pagination to fetch all entries.
253/// You can use methods from [`TryStreamExt`][try_stream_ext] or [`StreamExt`][stream_ext]
254/// to work with this.
255///
256/// [followers]: ClientExt::followers
257/// [local_notes]: ClientExt::local_notes
258/// [stream]: futures::stream::Stream
259/// [try_stream_ext]: futures::stream::TryStreamExt
260/// [stream_ext]: futures::stream::StreamExt
261pub trait ClientExt: Client + Sync {
262 // {{{ User
263 /// Gets the information of the user who is logged in with this client.
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// # use misskey_util::ClientExt;
269 /// # #[tokio::main]
270 /// # async fn main() -> anyhow::Result<()> {
271 /// # let client = misskey_test::test_client().await?;
272 /// let me = client.me().await?;
273 /// println!("Logged in as @{}", me.username);
274 /// # Ok(())
275 /// # }
276 /// ```
277 fn me(&self) -> BoxFuture<Result<User, Error<Self::Error>>> {
278 Box::pin(async move {
279 let user = self
280 .request(endpoint::i::Request::default())
281 .await
282 .map_err(Error::Client)?
283 .into_result()?;
284 Ok(user)
285 })
286 }
287
288 /// Updates the user logged in with this client.
289 ///
290 /// This method actually returns a builder, namely [`MeUpdateBuilder`].
291 /// You can chain the method calls to it corresponding to the fields you want to update.
292 /// Finally, calling [`update`][builder_update] method will actually perform the update.
293 /// See [`MeUpdateBuilder`] for the fields that can be updated.
294 ///
295 /// [builder_update]: MeUpdateBuilder::update
296 ///
297 /// # Examples
298 ///
299 /// ```
300 /// # use misskey_util::ClientExt;
301 /// # #[tokio::main]
302 /// # async fn main() -> anyhow::Result<()> {
303 /// # let client = misskey_test::test_client().await?;
304 /// // Flag it as a bot and set the name to "my awesome bot"
305 /// let updated = client
306 /// .update_me()
307 /// .bot(true)
308 /// .set_name("my awesome bot")
309 /// .update()
310 /// .await?;
311 ///
312 /// assert!(updated.is_bot);
313 /// assert_eq!(updated.name.unwrap(), "my awesome bot");
314 /// # Ok(())
315 /// # }
316 /// ```
317 fn update_me(&self) -> MeUpdateBuilder<&Self> {
318 MeUpdateBuilder::new(self)
319 }
320
321 /// Follows the specified user.
322 fn follow(&self, user: impl EntityRef<User>) -> BoxFuture<Result<User, Error<Self::Error>>> {
323 let user_id = user.entity_ref();
324 Box::pin(async move {
325 let user = self
326 .request(endpoint::following::create::Request { user_id })
327 .await
328 .map_err(Error::Client)?
329 .into_result()?;
330 Ok(user)
331 })
332 }
333
334 /// Unfollows the specified user.
335 fn unfollow(&self, user: impl EntityRef<User>) -> BoxFuture<Result<User, Error<Self::Error>>> {
336 let user_id = user.entity_ref();
337 Box::pin(async move {
338 let user = self
339 .request(endpoint::following::delete::Request { user_id })
340 .await
341 .map_err(Error::Client)?
342 .into_result()?;
343 Ok(user)
344 })
345 }
346
347 /// Mutes the specified user.
348 fn mute(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
349 let user_id = user.entity_ref();
350 Box::pin(async move {
351 self.request(endpoint::mute::create::Request { user_id })
352 .await
353 .map_err(Error::Client)?
354 .into_result()?;
355 Ok(())
356 })
357 }
358
359 /// Unmutes the specified user.
360 fn unmute(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
361 let user_id = user.entity_ref();
362 Box::pin(async move {
363 self.request(endpoint::mute::delete::Request { user_id })
364 .await
365 .map_err(Error::Client)?
366 .into_result()?;
367 Ok(())
368 })
369 }
370
371 /// Blocks the specified user.
372 fn block(&self, user: impl EntityRef<User>) -> BoxFuture<Result<User, Error<Self::Error>>> {
373 let user_id = user.entity_ref();
374 Box::pin(async move {
375 let user = self
376 .request(endpoint::blocking::create::Request { user_id })
377 .await
378 .map_err(Error::Client)?
379 .into_result()?;
380 Ok(user)
381 })
382 }
383
384 /// Unblocks the specified user.
385 fn unblock(&self, user: impl EntityRef<User>) -> BoxFuture<Result<User, Error<Self::Error>>> {
386 let user_id = user.entity_ref();
387 Box::pin(async move {
388 let user = self
389 .request(endpoint::blocking::delete::Request { user_id })
390 .await
391 .map_err(Error::Client)?
392 .into_result()?;
393 Ok(user)
394 })
395 }
396
397 /// Lists the followers of the specified user.
398 ///
399 /// # Examples
400 ///
401 /// This example uses [`TryStreamExt::try_next`][try_next] and [`while let`][while_let]
402 /// to retrieve notifications one after another until there are no more.
403 ///
404 /// [try_next]: futures::stream::TryStreamExt::try_next
405 /// [while_let]: https://doc.rust-lang.org/std/keyword.while.html
406 ///
407 /// ```
408 /// # use misskey_util::ClientExt;
409 /// # #[tokio::main]
410 /// # async fn main() -> anyhow::Result<()> {
411 /// # let client = misskey_test::test_client().await?;
412 /// use futures::stream::{StreamExt, TryStreamExt};
413 ///
414 /// // In this example, we will fetch all the followers and follow them.
415 /// // First, obtain your information to pass to `.follwers` method.
416 /// let me = client.me().await?;
417 ///
418 /// // `follwers` variable here is a `Stream` to enumerate first 50 followers of `me`.
419 /// let mut followers = client.followers(&me).take(50);
420 ///
421 /// // Retrieve all followers until there are no more.
422 /// while let Some(user) = followers.try_next().await? {
423 /// // Follow the `user` if you haven't already.
424 /// if !client.is_following(&user).await? {
425 /// client.follow(&user).await?;
426 /// }
427 /// }
428 /// # Ok(())
429 /// # }
430 /// ```
431 fn followers(&self, user: impl EntityRef<User>) -> PagerStream<BoxPager<Self, User>> {
432 let pager = BackwardPager::new(
433 self,
434 endpoint::users::followers::RequestWithUserId::builder()
435 .user_id(user.entity_ref())
436 .build(),
437 )
438 .map_ok(|v| v.into_iter().map(|f| f.follower).collect());
439 PagerStream::new(Box::pin(pager))
440 }
441
442 /// Lists the users that the specified user is following.
443 fn following(&self, user: impl EntityRef<User>) -> PagerStream<BoxPager<Self, User>> {
444 let pager = BackwardPager::new(
445 self,
446 endpoint::users::following::RequestWithUserId::builder()
447 .user_id(user.entity_ref())
448 .build(),
449 )
450 .map_ok(|v| v.into_iter().map(|f| f.followee).collect());
451 PagerStream::new(Box::pin(pager))
452 }
453
454 /// Pins the specified note to the profile.
455 fn pin_note(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<User, Error<Self::Error>>> {
456 let note_id = note.entity_ref();
457 Box::pin(async move {
458 let user = self
459 .request(endpoint::i::pin::Request { note_id })
460 .await
461 .map_err(Error::Client)?
462 .into_result()?;
463 Ok(user)
464 })
465 }
466
467 /// Unpins the specified note to the profile.
468 fn unpin_note(
469 &self,
470 note: impl EntityRef<Note>,
471 ) -> BoxFuture<Result<User, Error<Self::Error>>> {
472 let note_id = note.entity_ref();
473 Box::pin(async move {
474 let user = self
475 .request(endpoint::i::unpin::Request { note_id })
476 .await
477 .map_err(Error::Client)?
478 .into_result()?;
479 Ok(user)
480 })
481 }
482
483 /// Lists the follow requests sent to the user logged in with this client.
484 ///
485 /// # Examples
486 ///
487 /// ```
488 /// # use misskey_util::ClientExt;
489 /// # #[tokio::main]
490 /// # async fn main() -> anyhow::Result<()> {
491 /// # let client = misskey_test::test_client().await?;
492 /// // Accept all follow requests
493 /// for request in client.follow_requests().await? {
494 /// client.accept_follow_request(&request.follower).await?;
495 /// }
496 /// # Ok(())
497 /// # }
498 /// ```
499 fn follow_requests(&self) -> BoxFuture<Result<Vec<FollowRequest>, Error<Self::Error>>> {
500 Box::pin(async move {
501 let requests = self
502 .request(endpoint::following::requests::list::Request::default())
503 .await
504 .map_err(Error::Client)?
505 .into_result()?;
506 Ok(requests)
507 })
508 }
509
510 /// Cancels the follow request being sent to the specified user.
511 fn cancel_follow_request(
512 &self,
513 user: impl EntityRef<User>,
514 ) -> BoxFuture<Result<User, Error<Self::Error>>> {
515 let user_id = user.entity_ref();
516 Box::pin(async move {
517 let user = self
518 .request(endpoint::following::requests::cancel::Request { user_id })
519 .await
520 .map_err(Error::Client)?
521 .into_result()?;
522 Ok(user)
523 })
524 }
525
526 /// Accepts the follow request that have been received from the specified user.
527 fn accept_follow_request(
528 &self,
529 user: impl EntityRef<User>,
530 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
531 let user_id = user.entity_ref();
532 Box::pin(async move {
533 self.request(endpoint::following::requests::accept::Request { user_id })
534 .await
535 .map_err(Error::Client)?
536 .into_result()?;
537 Ok(())
538 })
539 }
540
541 /// Rejects the follow request that have been received from the specified user.
542 fn reject_follow_request(
543 &self,
544 user: impl EntityRef<User>,
545 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
546 let user_id = user.entity_ref();
547 Box::pin(async move {
548 self.request(endpoint::following::requests::reject::Request { user_id })
549 .await
550 .map_err(Error::Client)?
551 .into_result()?;
552 Ok(())
553 })
554 }
555
556 /// Lists the users muted by the user logged in with this client.
557 fn muting_users(&self) -> PagerStream<BoxPager<Self, User>> {
558 let pager = BackwardPager::new(self, endpoint::mute::list::Request::default())
559 .map_ok(|v| v.into_iter().map(|m| m.mutee).collect());
560 PagerStream::new(Box::pin(pager))
561 }
562
563 /// Lists the users blocked by the user logged in with this client.
564 fn blocking_users(&self) -> PagerStream<BoxPager<Self, User>> {
565 let pager = BackwardPager::new(self, endpoint::blocking::list::Request::default())
566 .map_ok(|v| v.into_iter().map(|b| b.blockee).collect());
567 PagerStream::new(Box::pin(pager))
568 }
569
570 /// Lists the notes favorited by the user logged in with this client.
571 fn favorited_notes(&self) -> PagerStream<BoxPager<Self, Note>> {
572 let pager = BackwardPager::new(self, endpoint::i::favorites::Request::default())
573 .map_ok(|v| v.into_iter().map(|f| f.note).collect());
574 PagerStream::new(Box::pin(pager))
575 }
576
577 /// Lists the notifications to the user logged in with this client.
578 ///
579 /// # Examples
580 ///
581 /// This example uses [`TryStreamExt::try_next`][try_next] and [`while let`][while_let]
582 /// to retrieve notifications one after another until there are no more.
583 ///
584 /// [try_next]: futures::stream::TryStreamExt::try_next
585 /// [while_let]: https://doc.rust-lang.org/std/keyword.while.html
586 ///
587 /// ```
588 /// # use misskey_util::ClientExt;
589 /// # #[tokio::main]
590 /// # async fn main() -> anyhow::Result<()> {
591 /// # let client = misskey_test::test_client().await?;
592 /// use futures::stream::{StreamExt, TryStreamExt};
593 ///
594 /// // `notifications` here is a `Stream` to enumerate first 10 notifications.
595 /// let mut notifications = client.notifications().take(10);
596 /// // Retrieve notifications until there are no more.
597 /// while let Some(notification) = notifications.try_next().await? {
598 /// // Print some information about the notification.
599 /// println!("notification {}: created at {}", notification.id, notification.created_at);
600 /// }
601 /// # Ok(())
602 /// # }
603 /// ```
604 fn notifications(&self) -> PagerStream<BoxPager<Self, Notification>> {
605 let pager = BackwardPager::new(self, endpoint::i::notifications::Request::default());
606 PagerStream::new(Box::pin(pager))
607 }
608
609 /// Returns the relationship between the specified user and the user logged in with this
610 /// client.
611 ///
612 /// The returned [`UserRelation`] object records the various information.
613 /// There are separate methods to examine each relationship (e.g.
614 /// [`is_following`][is_following]), so if you are only interested in one relationship,
615 /// it can be simpler to use those.
616 ///
617 /// [is_following]: ClientExt::is_following
618 fn user_relation(
619 &self,
620 user: impl EntityRef<User>,
621 ) -> BoxFuture<Result<UserRelation, Error<Self::Error>>> {
622 let user_id = user.entity_ref();
623 Box::pin(async move {
624 let relation = self
625 .request(endpoint::users::relation::Request { user_id })
626 .await
627 .map_err(Error::Client)?
628 .into_result()?;
629 Ok(relation)
630 })
631 }
632
633 /// Checks if the specified user is followed by the user logged in with this client.
634 ///
635 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
636 ///
637 /// [user_relation]: ClientExt::user_relation
638 ///
639 /// ```
640 /// # use misskey_util::ClientExt;
641 /// # use futures::stream::TryStreamExt;
642 /// # #[tokio::main]
643 /// # async fn main() -> anyhow::Result<()> {
644 /// # let client = misskey_test::test_client().await?;
645 /// # let user = client.users().list().try_next().await?.unwrap();
646 /// let relation = client.user_relation(&user).await?;
647 /// assert_eq!(client.is_following(&user).await?, relation.is_following);
648 /// # Ok(())
649 /// # }
650 /// ```
651 fn is_following(
652 &self,
653 user: impl EntityRef<User>,
654 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
655 let user_id = user.entity_ref();
656 Box::pin(async move { Ok(self.user_relation(user_id).await?.is_following) })
657 }
658
659 /// Checks if the specified user follows the user logged in with this client.
660 ///
661 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
662 ///
663 /// [user_relation]: ClientExt::user_relation
664 ///
665 /// ```
666 /// # use misskey_util::ClientExt;
667 /// # use futures::stream::TryStreamExt;
668 /// # #[tokio::main]
669 /// # async fn main() -> anyhow::Result<()> {
670 /// # let client = misskey_test::test_client().await?;
671 /// # let user = client.users().list().try_next().await?.unwrap();
672 /// let relation = client.user_relation(&user).await?;
673 /// assert_eq!(client.is_followed(&user).await?, relation.is_followed);
674 /// # Ok(())
675 /// # }
676 /// ```
677 fn is_followed(
678 &self,
679 user: impl EntityRef<User>,
680 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
681 let user_id = user.entity_ref();
682 Box::pin(async move { Ok(self.user_relation(user_id).await?.is_followed) })
683 }
684
685 /// Checks if the specified user is blocked by the user logged in with this client.
686 ///
687 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
688 ///
689 /// [user_relation]: ClientExt::user_relation
690 ///
691 /// ```
692 /// # use misskey_util::ClientExt;
693 /// # use futures::stream::TryStreamExt;
694 /// # #[tokio::main]
695 /// # async fn main() -> anyhow::Result<()> {
696 /// # let client = misskey_test::test_client().await?;
697 /// # let user = client.users().list().try_next().await?.unwrap();
698 /// let relation = client.user_relation(&user).await?;
699 /// assert_eq!(client.is_blocking(&user).await?, relation.is_blocking);
700 /// # Ok(())
701 /// # }
702 /// ```
703 fn is_blocking(
704 &self,
705 user: impl EntityRef<User>,
706 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
707 let user_id = user.entity_ref();
708 Box::pin(async move { Ok(self.user_relation(user_id).await?.is_blocking) })
709 }
710
711 /// Checks if the specified user blocks the user logged in with this client.
712 ///
713 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
714 ///
715 /// [user_relation]: ClientExt::user_relation
716 ///
717 /// ```
718 /// # use misskey_util::ClientExt;
719 /// # use futures::stream::TryStreamExt;
720 /// # #[tokio::main]
721 /// # async fn main() -> anyhow::Result<()> {
722 /// # let client = misskey_test::test_client().await?;
723 /// # let user = client.users().list().try_next().await?.unwrap();
724 /// let relation = client.user_relation(&user).await?;
725 /// assert_eq!(client.is_blocked(&user).await?, relation.is_blocked);
726 /// # Ok(())
727 /// # }
728 /// ```
729 fn is_blocked(
730 &self,
731 user: impl EntityRef<User>,
732 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
733 let user_id = user.entity_ref();
734 Box::pin(async move { Ok(self.user_relation(user_id).await?.is_blocked) })
735 }
736
737 /// Checks if the specified user is muted by the user logged in with this client.
738 ///
739 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
740 ///
741 /// [user_relation]: ClientExt::user_relation
742 ///
743 /// ```
744 /// # use misskey_util::ClientExt;
745 /// # use futures::stream::TryStreamExt;
746 /// # #[tokio::main]
747 /// # async fn main() -> anyhow::Result<()> {
748 /// # let client = misskey_test::test_client().await?;
749 /// # let user = client.users().list().try_next().await?.unwrap();
750 /// let relation = client.user_relation(&user).await?;
751 /// assert_eq!(client.is_muted(&user).await?, relation.is_muted);
752 /// # Ok(())
753 /// # }
754 /// ```
755 fn is_muted(&self, user: impl EntityRef<User>) -> BoxFuture<Result<bool, Error<Self::Error>>> {
756 let user_id = user.entity_ref();
757 Box::pin(async move { Ok(self.user_relation(user_id).await?.is_muted) })
758 }
759
760 /// Checks if the specified user has a pending follow request from the user logged in with this client.
761 ///
762 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
763 ///
764 /// [user_relation]: ClientExt::user_relation
765 ///
766 /// ```
767 /// # use misskey_util::ClientExt;
768 /// # use futures::stream::TryStreamExt;
769 /// # #[tokio::main]
770 /// # async fn main() -> anyhow::Result<()> {
771 /// # let client = misskey_test::test_client().await?;
772 /// # let user = client.users().list().try_next().await?.unwrap();
773 /// let relation = client.user_relation(&user).await?;
774 /// assert_eq!(client.has_pending_follow_request_from_me(&user).await?, relation.has_pending_follow_request_from_you);
775 /// # Ok(())
776 /// # }
777 /// ```
778 fn has_pending_follow_request_from_me(
779 &self,
780 user: impl EntityRef<User>,
781 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
782 let user_id = user.entity_ref();
783 Box::pin(async move {
784 Ok(self
785 .user_relation(user_id)
786 .await?
787 .has_pending_follow_request_from_you)
788 })
789 }
790
791 /// Checks if the specified user has a pending follow request to the user logged in with this client.
792 ///
793 /// If you are also interested in other relationships, use [`user_relation`][user_relation].
794 ///
795 /// [user_relation]: ClientExt::user_relation
796 ///
797 /// ```
798 /// # use misskey_util::ClientExt;
799 /// # use futures::stream::TryStreamExt;
800 /// # #[tokio::main]
801 /// # async fn main() -> anyhow::Result<()> {
802 /// # let client = misskey_test::test_client().await?;
803 /// # let user = client.users().list().try_next().await?.unwrap();
804 /// let relation = client.user_relation(&user).await?;
805 /// assert_eq!(client.has_pending_follow_request_to_me(&user).await?, relation.has_pending_follow_request_to_you);
806 /// # Ok(())
807 /// # }
808 /// ```
809 fn has_pending_follow_request_to_me(
810 &self,
811 user: impl EntityRef<User>,
812 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
813 let user_id = user.entity_ref();
814 Box::pin(async move {
815 Ok(self
816 .user_relation(user_id)
817 .await?
818 .has_pending_follow_request_to_you)
819 })
820 }
821
822 /// Gets the corresponding user from the ID.
823 fn get_user(&self, id: Id<User>) -> BoxFuture<Result<User, Error<Self::Error>>> {
824 Box::pin(async move {
825 let note = self
826 .request(endpoint::users::show::Request::WithUserId { user_id: id })
827 .await
828 .map_err(Error::Client)?
829 .into_result()?;
830 Ok(note)
831 })
832 }
833
834 /// Gets the list of corresponding user from the list of IDs.
835 fn get_users(
836 &self,
837 ids: impl IntoIterator<Item = Id<User>>,
838 ) -> BoxFuture<Result<Vec<User>, Error<Self::Error>>> {
839 let user_ids = ids.into_iter().collect();
840 Box::pin(async move {
841 let note = self
842 .request(endpoint::users::show::RequestWithUserIds { user_ids })
843 .await
844 .map_err(Error::Client)?
845 .into_result()?;
846 Ok(note)
847 })
848 }
849
850 /// Reports abuse by the specified user.
851 fn report_abuse(
852 &self,
853 user: impl EntityRef<User>,
854 comment: impl Into<String>,
855 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
856 let user_id = user.entity_ref();
857 let comment = comment.into();
858 Box::pin(async move {
859 self.request(endpoint::users::report_abuse::Request { user_id, comment })
860 .await
861 .map_err(Error::Client)?
862 .into_result()?;
863 Ok(())
864 })
865 }
866
867 /// Searches for users with the specified query string.
868 fn search_users(&self, query: impl Into<String>) -> PagerStream<BoxPager<Self, User>> {
869 let pager = OffsetPager::new(
870 self,
871 endpoint::users::search::Request::builder()
872 .query(query)
873 .build(),
874 );
875 PagerStream::new(Box::pin(pager))
876 }
877
878 /// Lists the users in the instance.
879 ///
880 /// This method actually returns a builder, namely [`UserListBuilder`].
881 /// You can specify how you want to list users by chaining methods.
882 /// The [`list`][builder_list] method of the builder returns a [`Stream`][stream]
883 /// that lists users in the specified way.
884 ///
885 /// [builder_list]: UserListBuilder::list
886 /// [stream]: futures::stream::Stream
887 ///
888 /// # Examples
889 ///
890 /// ```
891 /// # use misskey_util::ClientExt;
892 /// # #[tokio::main]
893 /// # async fn main() -> anyhow::Result<()> {
894 /// # let client = misskey_test::test_client().await?;
895 /// # use misskey_api as misskey;
896 /// use futures::stream::TryStreamExt;
897 /// use misskey::model::user::{User, UserSortKey};
898 ///
899 /// // Get a list of local moderator users sorted by number of followers.
900 /// let users: Vec<User> = client
901 /// .users()
902 /// .local()
903 /// .moderator()
904 /// .sort_by_followers()
905 /// .list()
906 /// .try_collect()
907 /// .await?;
908 /// # Ok(())
909 /// # }
910 /// ```
911 fn users(&self) -> UserListBuilder<&Self> {
912 UserListBuilder::new(self)
913 }
914
915 /// Lists the recommended users of the instance.
916 fn recommended_users(&self) -> PagerStream<BoxPager<Self, User>> {
917 let pager = OffsetPager::new(self, endpoint::users::recommendation::Request::default());
918 PagerStream::new(Box::pin(pager))
919 }
920
921 /// Lists the users who frequently reply to the specified user.
922 fn frequently_replied_users(
923 &self,
924 user: impl EntityRef<User>,
925 ) -> BoxFuture<Result<Vec<User>, Error<Self::Error>>> {
926 let user_id = user.entity_ref();
927 Box::pin(async move {
928 let users = self
929 .request(endpoint::users::get_frequently_replied_users::Request {
930 user_id,
931 limit: None,
932 })
933 .await
934 .map_err(Error::Client)?
935 .into_result()?
936 .into_iter()
937 .map(|endpoint::users::get_frequently_replied_users::Reply { user, .. }| user)
938 .collect();
939 Ok(users)
940 })
941 }
942
943 /// Lists the users pinned to the instance.
944 fn pinned_users(&self) -> BoxFuture<Result<Vec<User>, Error<Self::Error>>> {
945 Box::pin(async move {
946 let users = self
947 .request(endpoint::pinned_users::Request::default())
948 .await
949 .map_err(Error::Client)?
950 .into_result()?;
951 Ok(users)
952 })
953 }
954
955 // }}}
956
957 // {{{ Note
958 /// Returns a builder for composing a note.
959 ///
960 /// The returned builder provides methods to customize details of the note,
961 /// and you can chain them to compose a note incrementally.
962 /// Finally, calling [`create`][builder_create] method will actually create a note.
963 /// See [`NoteBuilder`] for the provided methods.
964 ///
965 /// [builder_create]: NoteBuilder::create
966 ///
967 /// # Examples
968 ///
969 /// ```
970 /// # use misskey_util::ClientExt;
971 /// # #[tokio::main]
972 /// # async fn main() -> anyhow::Result<()> {
973 /// # let client = misskey_test::test_client().await?;
974 /// let note = client
975 /// .build_note()
976 /// .text("Hello, World")
977 /// .followers_only()
978 /// .create()
979 /// .await?;
980 ///
981 /// assert_eq!(note.text.unwrap(), "Hello, World");
982 /// # Ok(())
983 /// # }
984 /// ```
985 fn build_note(&self) -> NoteBuilder<&Self> {
986 NoteBuilder::new(self)
987 }
988
989 /// Deletes the specified note.
990 ///
991 /// # Examples
992 ///
993 /// ```
994 /// # use misskey_util::ClientExt;
995 /// # #[tokio::main]
996 /// # async fn main() -> anyhow::Result<()> {
997 /// # let client = misskey_test::test_client().await?;
998 /// let note = client.create_note("Oops!").await?;
999 /// client.delete_note(¬e).await?;
1000 /// # Ok(())
1001 /// # }
1002 /// ```
1003 fn delete_note(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1004 let note_id = note.entity_ref();
1005 Box::pin(async move {
1006 self.request(endpoint::notes::delete::Request { note_id })
1007 .await
1008 .map_err(Error::Client)?
1009 .into_result()?;
1010 Ok(())
1011 })
1012 }
1013
1014 /// Gets the corresponding note from the ID.
1015 fn get_note(&self, id: Id<Note>) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1016 Box::pin(async move {
1017 let note = self
1018 .request(endpoint::notes::show::Request { note_id: id })
1019 .await
1020 .map_err(Error::Client)?
1021 .into_result()?;
1022 Ok(note)
1023 })
1024 }
1025
1026 /// Creates a note with the given text.
1027 ///
1028 /// # Examples
1029 ///
1030 /// ```
1031 /// # use misskey_util::ClientExt;
1032 /// # #[tokio::main]
1033 /// # async fn main() -> anyhow::Result<()> {
1034 /// # let client = misskey_test::test_client().await?;
1035 /// let note = client.create_note("Hello, Misskey!").await?;
1036 /// assert_eq!(note.text.unwrap(), "Hello, Misskey!");
1037 /// # Ok(())
1038 /// # }
1039 /// ```
1040 fn create_note(&self, text: impl Into<String>) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1041 let text = text.into();
1042 Box::pin(async move { self.build_note().text(text).create().await })
1043 }
1044
1045 /// Creates a poll with the given text and choices.
1046 ///
1047 /// # Examples
1048 ///
1049 /// ```
1050 /// # use misskey_util::ClientExt;
1051 /// # #[tokio::main]
1052 /// # async fn main() -> anyhow::Result<()> {
1053 /// # let client = misskey_test::test_client().await?;
1054 /// let note = client
1055 /// .poll("Which fruit is your favorite?", vec!["Apple", "Orange", "Banana"])
1056 /// .await?;
1057 /// # Ok(())
1058 /// # }
1059 /// ```
1060 fn poll(
1061 &self,
1062 text: impl Into<String>,
1063 choices: impl IntoIterator<Item = impl Into<String>>,
1064 ) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1065 let text = text.into();
1066 let choices: Vec<_> = choices.into_iter().map(Into::into).collect();
1067 Box::pin(async move { self.build_note().text(text).poll(choices).create().await })
1068 }
1069
1070 /// Creates a reply note with the given text.
1071 fn reply(
1072 &self,
1073 note: impl EntityRef<Note>,
1074 text: impl Into<String>,
1075 ) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1076 let note_id = note.entity_ref();
1077 let text = text.into();
1078 Box::pin(async move { self.build_note().reply(note_id).text(text).create().await })
1079 }
1080
1081 /// Creates a renote.
1082 fn renote(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1083 let note_id = note.entity_ref();
1084 Box::pin(async move { self.build_note().renote(note_id).create().await })
1085 }
1086
1087 /// Creates a quote note with the given text.
1088 fn quote(
1089 &self,
1090 note: impl EntityRef<Note>,
1091 text: impl Into<String>,
1092 ) -> BoxFuture<Result<Note, Error<Self::Error>>> {
1093 let note_id = note.entity_ref();
1094 let text = text.into();
1095 Box::pin(async move { self.build_note().renote(note_id).text(text).create().await })
1096 }
1097
1098 /// Adds the reaction to the specified note.
1099 fn react(
1100 &self,
1101 note: impl EntityRef<Note>,
1102 reaction: impl Into<Reaction>,
1103 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1104 let note_id = note.entity_ref();
1105 let reaction = reaction.into();
1106 Box::pin(async move {
1107 self.request(endpoint::notes::reactions::create::Request { note_id, reaction })
1108 .await
1109 .map_err(Error::Client)?
1110 .into_result()?;
1111 Ok(())
1112 })
1113 }
1114
1115 /// Deletes a reaction from the specified note.
1116 fn unreact(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1117 let note_id = note.entity_ref();
1118 Box::pin(async move {
1119 self.request(endpoint::notes::reactions::delete::Request { note_id })
1120 .await
1121 .map_err(Error::Client)?
1122 .into_result()?;
1123 Ok(())
1124 })
1125 }
1126
1127 /// Favorites the specified note.
1128 fn favorite(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1129 let note_id = note.entity_ref();
1130 Box::pin(async move {
1131 self.request(endpoint::notes::favorites::create::Request { note_id })
1132 .await
1133 .map_err(Error::Client)?
1134 .into_result()?;
1135 Ok(())
1136 })
1137 }
1138
1139 /// Unfavorites the specified note.
1140 fn unfavorite(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1141 let note_id = note.entity_ref();
1142 Box::pin(async move {
1143 self.request(endpoint::notes::favorites::delete::Request { note_id })
1144 .await
1145 .map_err(Error::Client)?
1146 .into_result()?;
1147 Ok(())
1148 })
1149 }
1150
1151 /// Watches the specified note.
1152 fn watch(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1153 let note_id = note.entity_ref();
1154 Box::pin(async move {
1155 self.request(endpoint::notes::watching::create::Request { note_id })
1156 .await
1157 .map_err(Error::Client)?
1158 .into_result()?;
1159 Ok(())
1160 })
1161 }
1162
1163 /// Unwatches the specified note.
1164 fn unwatch(&self, note: impl EntityRef<Note>) -> BoxFuture<Result<(), Error<Self::Error>>> {
1165 let note_id = note.entity_ref();
1166 Box::pin(async move {
1167 self.request(endpoint::notes::watching::delete::Request { note_id })
1168 .await
1169 .map_err(Error::Client)?
1170 .into_result()?;
1171 Ok(())
1172 })
1173 }
1174
1175 /// Checks if the specified note is favorited by the user logged in with this client.
1176 fn is_favorited(
1177 &self,
1178 note: impl EntityRef<Note>,
1179 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
1180 let note_id = note.entity_ref();
1181 Box::pin(async move {
1182 let state = self
1183 .request(endpoint::notes::state::Request { note_id })
1184 .await
1185 .map_err(Error::Client)?
1186 .into_result()?;
1187 Ok(state.is_favorited)
1188 })
1189 }
1190
1191 /// Checks if the specified note is watched by the user logged in with this client.
1192 fn is_watched(
1193 &self,
1194 note: impl EntityRef<Note>,
1195 ) -> BoxFuture<Result<bool, Error<Self::Error>>> {
1196 let note_id = note.entity_ref();
1197 Box::pin(async move {
1198 let state = self
1199 .request(endpoint::notes::state::Request { note_id })
1200 .await
1201 .map_err(Error::Client)?
1202 .into_result()?;
1203 Ok(state.is_watching)
1204 })
1205 }
1206
1207 /// Vote on the specified note.
1208 fn vote(
1209 &self,
1210 note: impl EntityRef<Note>,
1211 choice: u64,
1212 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1213 let note_id = note.entity_ref();
1214 Box::pin(async move {
1215 self.request(endpoint::notes::polls::vote::Request { note_id, choice })
1216 .await
1217 .map_err(Error::Client)?
1218 .into_result()?;
1219 Ok(())
1220 })
1221 }
1222
1223 /// Lists the featured notes.
1224 fn featured_notes(&self) -> PagerStream<BoxPager<Self, Note>> {
1225 let pager = OffsetPager::new(self, endpoint::notes::featured::Request::default());
1226 PagerStream::new(Box::pin(pager))
1227 }
1228
1229 /// Lists the notes of the conversation.
1230 fn conversation(&self, note: impl EntityRef<Note>) -> PagerStream<BoxPager<Self, Note>> {
1231 let pager = OffsetPager::new(
1232 self,
1233 endpoint::notes::conversation::Request::builder()
1234 .note_id(note.entity_ref())
1235 .build(),
1236 );
1237 PagerStream::new(Box::pin(pager))
1238 }
1239
1240 /// Lists the reply notes to the specified note.
1241 fn children_notes(&self, note: impl EntityRef<Note>) -> PagerStream<BoxPager<Self, Note>> {
1242 let pager = BackwardPager::new(
1243 self,
1244 endpoint::notes::children::Request::builder()
1245 .note_id(note.entity_ref())
1246 .build(),
1247 );
1248 PagerStream::new(Box::pin(pager))
1249 }
1250
1251 /// Lists the notes that are mentioning the account you are logged into with this client.
1252 fn mentioned_notes(&self) -> PagerStream<BoxPager<Self, Note>> {
1253 let pager = BackwardPager::new(self, endpoint::notes::mentions::Request::default());
1254 PagerStream::new(Box::pin(pager))
1255 }
1256
1257 /// Lists the renotes of the specified note.
1258 fn renotes(&self, note: impl EntityRef<Note>) -> PagerStream<BoxPager<Self, Note>> {
1259 let pager = BackwardPager::new(
1260 self,
1261 endpoint::notes::renotes::Request::builder()
1262 .note_id(note.entity_ref())
1263 .build(),
1264 );
1265 PagerStream::new(Box::pin(pager))
1266 }
1267
1268 /// Lists the replies to the specified note.
1269 fn replies(&self, note: impl EntityRef<Note>) -> PagerStream<BoxPager<Self, Note>> {
1270 let pager = BackwardPager::new(
1271 self,
1272 endpoint::notes::renotes::Request::builder()
1273 .note_id(note.entity_ref())
1274 .build(),
1275 );
1276 PagerStream::new(Box::pin(pager))
1277 }
1278
1279 /// Searches for notes with the specified query string.
1280 fn search_notes(&self, query: impl Into<String>) -> PagerStream<BoxPager<Self, Note>> {
1281 let pager = BackwardPager::new(
1282 self,
1283 endpoint::notes::search::Request::builder()
1284 .query(query)
1285 .build(),
1286 );
1287 PagerStream::new(Box::pin(pager))
1288 }
1289
1290 impl_timeline_method! { local, notes::local_timeline }
1291 impl_timeline_method! { global, notes::global_timeline }
1292 impl_timeline_method! { social, notes::hybrid_timeline }
1293 impl_timeline_method! { home, notes::timeline }
1294 impl_timeline_method! { user, users::notes, user_id = user : User }
1295 impl_timeline_method! { user_list, notes::user_list_timeline, list_id = list : UserList }
1296
1297 #[cfg(feature = "12-47-0")]
1298 impl_timeline_method! { channel, channels::timeline, channel_id = channel : Channel }
1299
1300 /// Lists the notes with tags as specified in the given query.
1301 ///
1302 /// # Examples
1303 ///
1304 /// ```
1305 /// # use misskey_util::ClientExt;
1306 /// # #[tokio::main]
1307 /// # async fn main() -> anyhow::Result<()> {
1308 /// # let client = misskey_test::test_client().await?;
1309 /// // Get all notes with the "linux" tag.
1310 /// let mut notes = client.tagged_notes("linux");
1311 /// # Ok(())
1312 /// # }
1313 /// ```
1314 ///
1315 /// ```
1316 /// # use misskey_util::ClientExt;
1317 /// # #[tokio::main]
1318 /// # async fn main() -> anyhow::Result<()> {
1319 /// # let client = misskey_test::test_client().await?;
1320 /// # use misskey_api as misskey;
1321 /// use misskey::model::query::Query;
1322 ///
1323 /// // Get all notes tagged with "test" or "bot".
1324 /// let mut notes = client.tagged_notes(Query::atom("test").or("bot"));
1325 /// # Ok(())
1326 /// # }
1327 /// ```
1328 fn tagged_notes(&self, query: impl Into<Query<Tag>>) -> PagerStream<BoxPager<Self, Note>> {
1329 let pager = BackwardPager::new(
1330 self,
1331 endpoint::notes::search_by_tag::Request::builder()
1332 .query(query)
1333 .build(),
1334 );
1335 PagerStream::new(Box::pin(pager))
1336 }
1337
1338 /// Lists the local notes with the given file types.
1339 ///
1340 /// # Examples
1341 ///
1342 /// ```
1343 /// # use misskey_util::ClientExt;
1344 /// # #[tokio::main]
1345 /// # async fn main() -> anyhow::Result<()> {
1346 /// # let client = misskey_test::test_client().await?;
1347 /// use mime::IMAGE_STAR;
1348 ///
1349 /// // Get all local notes with image files.
1350 /// let mut notes = client.local_notes_with_file_types(vec![IMAGE_STAR]);
1351 /// # Ok(())
1352 /// # }
1353 /// ```
1354 fn local_notes_with_file_types(
1355 &self,
1356 types: impl IntoIterator<Item = Mime>,
1357 ) -> PagerStream<BoxPager<Self, Note>> {
1358 let pager = BackwardPager::new(
1359 self,
1360 endpoint::notes::local_timeline::Request::builder()
1361 .file_type(types.into_iter().map(Into::into).collect())
1362 .build(),
1363 );
1364 PagerStream::new(Box::pin(pager))
1365 }
1366 // }}}
1367
1368 // {{{ User List
1369 /// Creates a user list with the given name.
1370 ///
1371 /// # Examples
1372 ///
1373 /// ```
1374 /// # use misskey_util::ClientExt;
1375 /// # #[tokio::main]
1376 /// # async fn main() -> anyhow::Result<()> {
1377 /// # let client = misskey_test::test_client().await?;
1378 /// let list = client.create_user_list("list").await?;
1379 /// assert_eq!(list.name, "list");
1380 /// # Ok(())
1381 /// # }
1382 /// ```
1383 fn create_user_list(
1384 &self,
1385 name: impl Into<String>,
1386 ) -> BoxFuture<Result<UserList, Error<Self::Error>>> {
1387 let name = name.into();
1388 Box::pin(async move {
1389 let list = self
1390 .request(endpoint::users::lists::create::Request { name })
1391 .await
1392 .map_err(Error::Client)?
1393 .into_result()?;
1394 Ok(list)
1395 })
1396 }
1397
1398 /// Deletes the specified user list.
1399 ///
1400 /// # Examples
1401 ///
1402 /// ```
1403 /// # use misskey_util::ClientExt;
1404 /// # #[tokio::main]
1405 /// # async fn main() -> anyhow::Result<()> {
1406 /// # let client = misskey_test::test_client().await?;
1407 /// let list = client.create_user_list("list").await?;
1408 /// client.delete_user_list(&list).await?;
1409 /// # Ok(())
1410 /// # }
1411 /// ```
1412 fn delete_user_list(
1413 &self,
1414 list: impl EntityRef<UserList>,
1415 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1416 let list_id = list.entity_ref();
1417 Box::pin(async move {
1418 self.request(endpoint::users::lists::delete::Request { list_id })
1419 .await
1420 .map_err(Error::Client)?
1421 .into_result()?;
1422 Ok(())
1423 })
1424 }
1425
1426 /// Updates the name of the specified user list to the given one.
1427 ///
1428 /// # Examples
1429 ///
1430 /// ```
1431 /// # use misskey_util::ClientExt;
1432 /// # #[tokio::main]
1433 /// # async fn main() -> anyhow::Result<()> {
1434 /// # let client = misskey_test::test_client().await?;
1435 /// let list = client.create_user_list("list").await?;
1436 /// let renamed_list = client.rename_user_list(&list, "list2").await?;
1437 /// assert_eq!(renamed_list.name, "list2");
1438 /// # Ok(())
1439 /// # }
1440 /// ```
1441 fn rename_user_list(
1442 &self,
1443 list: impl EntityRef<UserList>,
1444 name: impl Into<String>,
1445 ) -> BoxFuture<Result<UserList, Error<Self::Error>>> {
1446 let list_id = list.entity_ref();
1447 let name = name.into();
1448 Box::pin(async move {
1449 let list = self
1450 .request(endpoint::users::lists::update::Request { list_id, name })
1451 .await
1452 .map_err(Error::Client)?
1453 .into_result()?;
1454 Ok(list)
1455 })
1456 }
1457
1458 /// Gets the corresponding user list from the ID.
1459 fn get_user_list(&self, id: Id<UserList>) -> BoxFuture<Result<UserList, Error<Self::Error>>> {
1460 Box::pin(async move {
1461 let list = self
1462 .request(endpoint::users::lists::show::Request { list_id: id })
1463 .await
1464 .map_err(Error::Client)?
1465 .into_result()?;
1466 Ok(list)
1467 })
1468 }
1469
1470 /// Adds the user from the specified user list.
1471 fn push_to_user_list(
1472 &self,
1473 list: impl EntityRef<UserList>,
1474 user: impl EntityRef<User>,
1475 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1476 let list_id = list.entity_ref();
1477 let user_id = user.entity_ref();
1478 Box::pin(async move {
1479 self.request(endpoint::users::lists::push::Request { list_id, user_id })
1480 .await
1481 .map_err(Error::Client)?
1482 .into_result()?;
1483 Ok(())
1484 })
1485 }
1486
1487 /// Deletes the user from the specified user list.
1488 fn pull_from_user_list(
1489 &self,
1490 list: impl EntityRef<UserList>,
1491 user: impl EntityRef<User>,
1492 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1493 let list_id = list.entity_ref();
1494 let user_id = user.entity_ref();
1495 Box::pin(async move {
1496 self.request(endpoint::users::lists::pull::Request { list_id, user_id })
1497 .await
1498 .map_err(Error::Client)?
1499 .into_result()?;
1500 Ok(())
1501 })
1502 }
1503 // }}}
1504
1505 // {{{ User Group
1506 /// Creates a user group with the given name.
1507 ///
1508 /// # Examples
1509 ///
1510 /// ```
1511 /// # use misskey_util::ClientExt;
1512 /// # #[tokio::main]
1513 /// # async fn main() -> anyhow::Result<()> {
1514 /// # let client = misskey_test::test_client().await?;
1515 /// let group = client.create_user_group("group").await?;
1516 /// assert_eq!(group.name, "group");
1517 /// # Ok(())
1518 /// # }
1519 /// ```
1520 fn create_user_group(
1521 &self,
1522 name: impl Into<String>,
1523 ) -> BoxFuture<Result<UserGroup, Error<Self::Error>>> {
1524 let name = name.into();
1525 Box::pin(async move {
1526 let group = self
1527 .request(endpoint::users::groups::create::Request { name })
1528 .await
1529 .map_err(Error::Client)?
1530 .into_result()?;
1531 Ok(group)
1532 })
1533 }
1534
1535 /// Deletes the specified user group.
1536 ///
1537 /// # Examples
1538 ///
1539 /// ```
1540 /// # use misskey_util::ClientExt;
1541 /// # #[tokio::main]
1542 /// # async fn main() -> anyhow::Result<()> {
1543 /// # let client = misskey_test::test_client().await?;
1544 /// let group = client.create_user_group("group").await?;
1545 /// client.delete_user_group(&group).await?;
1546 /// # Ok(())
1547 /// # }
1548 /// ```
1549 fn delete_user_group(
1550 &self,
1551 group: impl EntityRef<UserGroup>,
1552 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1553 let group_id = group.entity_ref();
1554 Box::pin(async move {
1555 self.request(endpoint::users::groups::delete::Request { group_id })
1556 .await
1557 .map_err(Error::Client)?
1558 .into_result()?;
1559 Ok(())
1560 })
1561 }
1562
1563 /// Updates the name of the specified user group to the given one.
1564 ///
1565 /// # Examples
1566 ///
1567 /// ```
1568 /// # use misskey_util::ClientExt;
1569 /// # #[tokio::main]
1570 /// # async fn main() -> anyhow::Result<()> {
1571 /// # let client = misskey_test::test_client().await?;
1572 /// let group = client.create_user_group("group").await?;
1573 /// let renamed_group = client.rename_user_group(&group, "group2").await?;
1574 /// assert_eq!(renamed_group.name, "group2");
1575 /// # Ok(())
1576 /// # }
1577 /// ```
1578 fn rename_user_group(
1579 &self,
1580 group: impl EntityRef<UserGroup>,
1581 name: impl Into<String>,
1582 ) -> BoxFuture<Result<UserGroup, Error<Self::Error>>> {
1583 let group_id = group.entity_ref();
1584 let name = name.into();
1585 Box::pin(async move {
1586 let group = self
1587 .request(endpoint::users::groups::update::Request { group_id, name })
1588 .await
1589 .map_err(Error::Client)?
1590 .into_result()?;
1591 Ok(group)
1592 })
1593 }
1594
1595 /// Gets the corresponding user group from the ID.
1596 fn get_user_group(
1597 &self,
1598 id: Id<UserGroup>,
1599 ) -> BoxFuture<Result<UserGroup, Error<Self::Error>>> {
1600 Box::pin(async move {
1601 let group = self
1602 .request(endpoint::users::groups::show::Request { group_id: id })
1603 .await
1604 .map_err(Error::Client)?
1605 .into_result()?;
1606 Ok(group)
1607 })
1608 }
1609
1610 /// Invites the user to the specified user group.
1611 fn invite_to_user_group(
1612 &self,
1613 group: impl EntityRef<UserGroup>,
1614 user: impl EntityRef<User>,
1615 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1616 let group_id = group.entity_ref();
1617 let user_id = user.entity_ref();
1618 Box::pin(async move {
1619 self.request(endpoint::users::groups::invite::Request { group_id, user_id })
1620 .await
1621 .map_err(Error::Client)?
1622 .into_result()?;
1623 Ok(())
1624 })
1625 }
1626
1627 /// Deletes the user from the specified user group.
1628 ///
1629 /// Note that the owner of the group cannot be deleted.
1630 /// If you want to do so, you first need to transfer the group with
1631 /// [`transfer_user_group`][transfer].
1632 ///
1633 /// [transfer]: ClientExt::transfer_user_group
1634 fn pull_from_user_group(
1635 &self,
1636 group: impl EntityRef<UserGroup>,
1637 user: impl EntityRef<User>,
1638 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1639 let group_id = group.entity_ref();
1640 let user_id = user.entity_ref();
1641 Box::pin(async move {
1642 self.request(endpoint::users::groups::pull::Request { group_id, user_id })
1643 .await
1644 .map_err(Error::Client)?
1645 .into_result()?;
1646 Ok(())
1647 })
1648 }
1649
1650 /// Transfers the specified user group.
1651 ///
1652 /// Note that you can only transfer the group to one of its members.
1653 fn transfer_user_group(
1654 &self,
1655 group: impl EntityRef<UserGroup>,
1656 user: impl EntityRef<User>,
1657 ) -> BoxFuture<Result<UserGroup, Error<Self::Error>>> {
1658 let group_id = group.entity_ref();
1659 let user_id = user.entity_ref();
1660 Box::pin(async move {
1661 let group = self
1662 .request(endpoint::users::groups::transfer::Request { group_id, user_id })
1663 .await
1664 .map_err(Error::Client)?
1665 .into_result()?;
1666 Ok(group)
1667 })
1668 }
1669
1670 /// Lists the user group invitations sent to the user who is logged in with this client.
1671 ///
1672 /// # Examples
1673 ///
1674 /// This example uses [`TryStreamExt::try_next`][try_next] and [`while let`][while_let]
1675 /// to retrieve invitations one after another until there are no more.
1676 ///
1677 /// [try_next]: futures::stream::TryStreamExt::try_next
1678 /// [while_let]: https://doc.rust-lang.org/std/keyword.while.html
1679 ///
1680 /// ```
1681 /// # use misskey_util::ClientExt;
1682 /// # #[tokio::main]
1683 /// # async fn main() -> anyhow::Result<()> {
1684 /// # let client = misskey_test::test_client().await?;
1685 /// use futures::stream::TryStreamExt;
1686 ///
1687 /// // `invitations` here is a `Stream` to enumerate all the invitations.
1688 /// let mut invitations = client.user_group_invitations();
1689 /// // Retrieve invitations until there are no more.
1690 /// while let Some(invitation) = invitations.try_next().await? {
1691 /// // Accept the invitation.
1692 /// client.accept_user_group_invitation(&invitation).await?;
1693 /// }
1694 /// # Ok(())
1695 /// # }
1696 /// ```
1697 fn user_group_invitations(&self) -> PagerStream<BoxPager<Self, UserGroupInvitation>> {
1698 let pager = BackwardPager::new(self, endpoint::i::user_group_invites::Request::default());
1699 PagerStream::new(Box::pin(pager))
1700 }
1701
1702 /// Accepts the specified user group invitation sent to the user logged in with this client.
1703 fn accept_user_group_invitation(
1704 &self,
1705 invitation: impl EntityRef<UserGroupInvitation>,
1706 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1707 let invitation_id = invitation.entity_ref();
1708 Box::pin(async move {
1709 self.request(endpoint::users::groups::invitations::accept::Request { invitation_id })
1710 .await
1711 .map_err(Error::Client)?
1712 .into_result()?;
1713 Ok(())
1714 })
1715 }
1716
1717 /// Rejects the specified user group invitation sent to the user logged in with this client.
1718 fn reject_user_group_invitation(
1719 &self,
1720 invitation: impl EntityRef<UserGroupInvitation>,
1721 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1722 let invitation_id = invitation.entity_ref();
1723 Box::pin(async move {
1724 self.request(endpoint::users::groups::invitations::reject::Request { invitation_id })
1725 .await
1726 .map_err(Error::Client)?
1727 .into_result()?;
1728 Ok(())
1729 })
1730 }
1731
1732 /// Lists the user groups joined by the user logged in with this client.
1733 fn joined_user_groups(&self) -> BoxFuture<Result<Vec<UserGroup>, Error<Self::Error>>> {
1734 Box::pin(async move {
1735 let groups = self
1736 .request(endpoint::users::groups::joined::Request::default())
1737 .await
1738 .map_err(Error::Client)?
1739 .into_result()?;
1740 Ok(groups)
1741 })
1742 }
1743
1744 /// Lists the user groups owned by the user logged in with this client.
1745 fn owned_user_groups(&self) -> BoxFuture<Result<Vec<UserGroup>, Error<Self::Error>>> {
1746 Box::pin(async move {
1747 let groups = self
1748 .request(endpoint::users::groups::owned::Request::default())
1749 .await
1750 .map_err(Error::Client)?
1751 .into_result()?;
1752 Ok(groups)
1753 })
1754 }
1755 // }}}
1756
1757 // {{{ Antenna
1758 /// Creates an antenna with the given name and query.
1759 ///
1760 /// # Examples
1761 ///
1762 /// ```
1763 /// # use misskey_util::ClientExt;
1764 /// # #[tokio::main]
1765 /// # async fn main() -> anyhow::Result<()> {
1766 /// # let client = misskey_test::test_client().await?;
1767 /// # use misskey_api as misskey;
1768 /// use misskey::model::query::Query;
1769 ///
1770 /// // Create an antenna for notes containing "misskey" or "msky"
1771 /// let antenna = client
1772 /// .create_antenna("misskey antenna", Query::atom("misskey").or("msky"))
1773 /// .await?;
1774 ///
1775 /// assert_eq!(antenna.name, "misskey antenna");
1776 /// # Ok(())
1777 /// # }
1778 /// ```
1779 fn create_antenna(
1780 &self,
1781 name: impl Into<String>,
1782 query: impl Into<Query<String>>,
1783 ) -> BoxFuture<Result<Antenna, Error<Self::Error>>> {
1784 let name = name.into();
1785 let query = query.into();
1786 Box::pin(async move {
1787 self.build_antenna()
1788 .name(name)
1789 .include(query)
1790 .create()
1791 .await
1792 })
1793 }
1794
1795 /// Returns a builder for creating an antenna.
1796 ///
1797 /// The returned builder provides methods to customize details of the antenna,
1798 /// and you can chain them to create an antenna incrementally.
1799 /// Finally, calling [`create`][builder_create] method will actually create an antenna.
1800 /// See [`AntennaBuilder`] for the provided methods.
1801 ///
1802 /// [builder_create]: AntennaBuilder::create
1803 ///
1804 /// # Examples
1805 ///
1806 /// ```
1807 /// # use misskey_util::ClientExt;
1808 /// # #[tokio::main]
1809 /// # async fn main() -> anyhow::Result<()> {
1810 /// # let client = misskey_test::test_client().await?;
1811 /// // Create an antenna for non-reply notes in home timeline that include "misskey"
1812 /// let antenna = client
1813 /// .build_antenna()
1814 /// .name("misskey antenna")
1815 /// .include("misskey")
1816 /// .home()
1817 /// .exclude_replies(true)
1818 /// .create()
1819 /// .await?;
1820 ///
1821 /// assert_eq!(antenna.name, "misskey antenna");
1822 /// # Ok(())
1823 /// # }
1824 /// ```
1825 fn build_antenna(&self) -> AntennaBuilder<&Self> {
1826 AntennaBuilder::new(self)
1827 }
1828
1829 /// Deletes the specified antenna.
1830 ///
1831 /// # Examples
1832 ///
1833 /// ```
1834 /// # use misskey_util::ClientExt;
1835 /// # #[tokio::main]
1836 /// # async fn main() -> anyhow::Result<()> {
1837 /// # let client = misskey_test::test_client().await?;
1838 /// let antenna = client
1839 /// .create_antenna("antenna", "misskey")
1840 /// .await?;
1841 /// client.delete_antenna(&antenna).await?;
1842 /// # Ok(())
1843 /// # }
1844 /// ```
1845 fn delete_antenna(
1846 &self,
1847 antenna: impl EntityRef<Antenna>,
1848 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
1849 let antenna_id = antenna.entity_ref();
1850 Box::pin(async move {
1851 self.request(endpoint::antennas::delete::Request { antenna_id })
1852 .await
1853 .map_err(Error::Client)?
1854 .into_result()?;
1855 Ok(())
1856 })
1857 }
1858
1859 /// Gets the corresponding antenna from the ID.
1860 fn get_antenna(&self, id: Id<Antenna>) -> BoxFuture<Result<Antenna, Error<Self::Error>>> {
1861 Box::pin(async move {
1862 let antenna = self
1863 .request(endpoint::antennas::show::Request { antenna_id: id })
1864 .await
1865 .map_err(Error::Client)?
1866 .into_result()?;
1867 Ok(antenna)
1868 })
1869 }
1870
1871 /// Updates the antenna.
1872 ///
1873 /// This method actually returns a builder, namely [`AntennaUpdateBuilder`].
1874 /// You can chain the method calls to it corresponding to the fields you want to update.
1875 /// Finally, calling [`update`][builder_update] method will actually perform the update.
1876 /// See [`AntennaUpdateBuilder`] for the fields that can be updated.
1877 ///
1878 /// [builder_update]: AntennaUpdateBuilder::update
1879 ///
1880 /// # Examples
1881 ///
1882 /// ```
1883 /// # use misskey_util::ClientExt;
1884 /// # #[tokio::main]
1885 /// # async fn main() -> anyhow::Result<()> {
1886 /// # let client = misskey_test::test_client().await?;
1887 /// let antenna = client
1888 /// .create_antenna("antenna", "misskey")
1889 /// .await?;
1890 ///
1891 /// // Change source and case sensitivity of the antenna
1892 /// client
1893 /// .update_antenna(antenna)
1894 /// .case_sensitive(true)
1895 /// .all()
1896 /// .update()
1897 /// .await?;
1898 /// # Ok(())
1899 /// # }
1900 /// ```
1901 fn update_antenna(&self, antenna: Antenna) -> AntennaUpdateBuilder<&Self> {
1902 AntennaUpdateBuilder::new(self, antenna)
1903 }
1904
1905 /// Lists the antennas created by the user logged in with this client.
1906 fn antennas(&self) -> BoxFuture<Result<Vec<Antenna>, Error<Self::Error>>> {
1907 Box::pin(async move {
1908 let antennas = self
1909 .request(endpoint::antennas::list::Request::default())
1910 .await
1911 .map_err(Error::Client)?
1912 .into_result()?;
1913 Ok(antennas)
1914 })
1915 }
1916
1917 /// Lists the notes that hit the specified antenna.
1918 fn antenna_notes(&self, antenna: impl EntityRef<Antenna>) -> PagerStream<BoxPager<Self, Note>> {
1919 let pager = BackwardPager::new(
1920 self,
1921 endpoint::antennas::notes::Request::builder()
1922 .antenna_id(antenna.entity_ref())
1923 .build(),
1924 );
1925 PagerStream::new(Box::pin(pager))
1926 }
1927 // }}}
1928
1929 // {{{ Channel
1930 /// Creates a channel with the given name.
1931 ///
1932 /// # Examples
1933 ///
1934 /// ```
1935 /// # use misskey_util::ClientExt;
1936 /// # #[tokio::main]
1937 /// # async fn main() -> anyhow::Result<()> {
1938 /// # let client = misskey_test::test_client().await?;
1939 /// let channel = client.create_channel("name").await?;
1940 /// assert_eq!(channel.name, "name");
1941 /// # Ok(())
1942 /// # }
1943 /// ```
1944 #[cfg(feature = "12-47-0")]
1945 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
1946 fn create_channel(
1947 &self,
1948 name: impl Into<String>,
1949 ) -> BoxFuture<Result<Channel, Error<Self::Error>>> {
1950 let name = name.into();
1951 Box::pin(async move { self.build_channel().name(name).create().await })
1952 }
1953
1954 /// Returns a builder for creating a channel.
1955 ///
1956 /// The returned builder provides methods to customize details of the channel,
1957 /// and you can chain them to create a channel incrementally.
1958 /// Finally, calling [`create`][builder_create] method will actually create a channel.
1959 /// See [`ChannelBuilder`] for the provided methods.
1960 ///
1961 /// [builder_create]: ChannelBuilder::create
1962 ///
1963 /// # Examples
1964 ///
1965 /// ```
1966 /// # use misskey_util::ClientExt;
1967 /// # #[tokio::main]
1968 /// # async fn main() -> anyhow::Result<()> {
1969 /// # let client = misskey_test::test_client().await?;
1970 /// let channel = client
1971 /// .build_channel()
1972 /// .name("bot devs")
1973 /// .description("Let's talk about Misskey's bot development!")
1974 /// .create()
1975 /// .await?;
1976 ///
1977 /// assert_eq!(channel.name, "bot devs");
1978 /// # Ok(())
1979 /// # }
1980 /// ```
1981 #[cfg(feature = "12-47-0")]
1982 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
1983 fn build_channel(&self) -> ChannelBuilder<&Self> {
1984 ChannelBuilder::new(self)
1985 }
1986
1987 /// Gets the corresponding channel from the ID.
1988 #[cfg(feature = "12-47-0")]
1989 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
1990 fn get_channel(&self, id: Id<Channel>) -> BoxFuture<Result<Channel, Error<Self::Error>>> {
1991 Box::pin(async move {
1992 let channel = self
1993 .request(endpoint::channels::show::Request { channel_id: id })
1994 .await
1995 .map_err(Error::Client)?
1996 .into_result()?;
1997 Ok(channel)
1998 })
1999 }
2000
2001 /// Updates the specified channel.
2002 ///
2003 /// This method actually returns a builder, namely [`ChannelUpdateBuilder`].
2004 /// You can chain the method calls to it corresponding to the fields you want to update.
2005 /// Finally, calling [`update`][builder_update] method will actually perform the update.
2006 /// See [`ChannelUpdateBuilder`] for the fields that can be updated.
2007 ///
2008 /// [builder_update]: ChannelUpdateBuilder::update
2009 ///
2010 /// # Examples
2011 ///
2012 /// ```
2013 /// # use misskey_util::ClientExt;
2014 /// # #[tokio::main]
2015 /// # async fn main() -> anyhow::Result<()> {
2016 /// # let client = misskey_test::test_client().await?;
2017 /// let channel = client.create_channel("feedback").await?;
2018 /// client
2019 /// .update_channel(&channel)
2020 /// .set_description("Give us feedback on the instance.")
2021 /// .update()
2022 /// .await?;
2023 /// # Ok(())
2024 /// # }
2025 /// ```
2026 #[cfg(feature = "12-47-0")]
2027 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
2028 fn update_channel(&self, channel: impl EntityRef<Channel>) -> ChannelUpdateBuilder<&Self> {
2029 ChannelUpdateBuilder::new(self, channel)
2030 }
2031
2032 /// Follows the specified channel.
2033 #[cfg(feature = "12-47-0")]
2034 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
2035 fn follow_channel(
2036 &self,
2037 channel: impl EntityRef<Channel>,
2038 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2039 let channel_id = channel.entity_ref();
2040 Box::pin(async move {
2041 self.request(endpoint::channels::follow::Request { channel_id })
2042 .await
2043 .map_err(Error::Client)?
2044 .into_result()?;
2045 Ok(())
2046 })
2047 }
2048
2049 /// Unfollows the specified channel.
2050 #[cfg(feature = "12-47-0")]
2051 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
2052 fn unfollow_channel(
2053 &self,
2054 channel: impl EntityRef<Channel>,
2055 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2056 let channel_id = channel.entity_ref();
2057 Box::pin(async move {
2058 self.request(endpoint::channels::unfollow::Request { channel_id })
2059 .await
2060 .map_err(Error::Client)?
2061 .into_result()?;
2062 Ok(())
2063 })
2064 }
2065
2066 /// Lists the channels followed by the user logged in with this client.
2067 #[cfg(feature = "12-48-0")]
2068 #[cfg_attr(docsrs, doc(cfg(feature = "12-48-0")))]
2069 fn followed_channels(&self) -> PagerStream<BoxPager<Self, Channel>> {
2070 let pager = BackwardPager::new(self, endpoint::channels::followed::Request::default());
2071 PagerStream::new(Box::pin(pager))
2072 }
2073
2074 /// Lists the channels owned by the user logged in with this client.
2075 #[cfg(feature = "12-48-0")]
2076 #[cfg_attr(docsrs, doc(cfg(feature = "12-48-0")))]
2077 fn owned_channels(&self) -> PagerStream<BoxPager<Self, Channel>> {
2078 let pager = BackwardPager::new(self, endpoint::channels::owned::Request::default());
2079 PagerStream::new(Box::pin(pager))
2080 }
2081
2082 /// Lists the featured channels.
2083 #[cfg(feature = "12-47-0")]
2084 #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
2085 fn featured_channels(&self) -> BoxFuture<Result<Vec<Channel>, Error<Self::Error>>> {
2086 Box::pin(async move {
2087 let channels = self
2088 .request(endpoint::channels::featured::Request::default())
2089 .await
2090 .map_err(Error::Client)?
2091 .into_result()?;
2092 Ok(channels)
2093 })
2094 }
2095 // }}}
2096
2097 // {{{ Clip
2098 /// Creates a clip with the given name.
2099 ///
2100 /// # Examples
2101 ///
2102 /// ```
2103 /// # use misskey_util::ClientExt;
2104 /// # #[tokio::main]
2105 /// # async fn main() -> anyhow::Result<()> {
2106 /// # let client = misskey_test::test_client().await?;
2107 /// let clip = client.create_clip("name").await?;
2108 /// assert_eq!(clip.name, "name");
2109 /// # Ok(())
2110 /// # }
2111 /// ```
2112 fn create_clip(&self, name: impl Into<String>) -> BoxFuture<Result<Clip, Error<Self::Error>>> {
2113 let name = name.into();
2114 #[cfg(not(feature = "12-57-0"))]
2115 let request = endpoint::clips::create::Request { name };
2116 #[cfg(feature = "12-57-0")]
2117 let request = endpoint::clips::create::Request {
2118 name,
2119 is_public: Some(false),
2120 description: None,
2121 };
2122 Box::pin(async move {
2123 let clip = self
2124 .request(request)
2125 .await
2126 .map_err(Error::Client)?
2127 .into_result()?;
2128 Ok(clip)
2129 })
2130 }
2131
2132 /// Returns a builder for creating a clip.
2133 ///
2134 /// The returned builder provides methods to customize details of the clip,
2135 /// and you can chain them to create a clip incrementally.
2136 /// Finally, calling [`create`][builder_create] method will actually create a clip.
2137 /// See [`ClipBuilder`] for the provided methods.
2138 ///
2139 /// [builder_create]: ClipBuilder::create
2140 ///
2141 /// # Examples
2142 ///
2143 /// ```
2144 /// # use misskey_util::ClientExt;
2145 /// # #[tokio::main]
2146 /// # async fn main() -> anyhow::Result<()> {
2147 /// # let client = misskey_test::test_client().await?;
2148 /// let clip = client
2149 /// .build_clip()
2150 /// .name("kawaii notes")
2151 /// .public(true)
2152 /// .create()
2153 /// .await?;
2154 ///
2155 /// assert_eq!(clip.name, "kawaii notes");
2156 /// # Ok(())
2157 /// # }
2158 /// ```
2159 #[cfg(feature = "12-57-0")]
2160 #[cfg_attr(docsrs, doc(cfg(feature = "12-57-0")))]
2161 fn build_clip(&self) -> ClipBuilder<&Self> {
2162 ClipBuilder::new(&self)
2163 }
2164
2165 /// Deletes the specified clip.
2166 ///
2167 /// # Examples
2168 ///
2169 /// ```
2170 /// # use misskey_util::ClientExt;
2171 /// # #[tokio::main]
2172 /// # async fn main() -> anyhow::Result<()> {
2173 /// # let client = misskey_test::test_client().await?;
2174 /// let clip = client.create_clip("Oops!").await?;
2175 /// client.delete_clip(&clip).await?;
2176 /// # Ok(())
2177 /// # }
2178 /// ```
2179 fn delete_clip(&self, clip: impl EntityRef<Clip>) -> BoxFuture<Result<(), Error<Self::Error>>> {
2180 let clip_id = clip.entity_ref();
2181 Box::pin(async move {
2182 self.request(endpoint::clips::delete::Request { clip_id })
2183 .await
2184 .map_err(Error::Client)?
2185 .into_result()?;
2186 Ok(())
2187 })
2188 }
2189
2190 /// Lists the clips created by the user logged in with this client.
2191 fn clips(&self) -> BoxFuture<Result<Vec<Clip>, Error<Self::Error>>> {
2192 Box::pin(async move {
2193 let clips = self
2194 .request(endpoint::clips::list::Request::default())
2195 .await
2196 .map_err(Error::Client)?
2197 .into_result()?;
2198 Ok(clips)
2199 })
2200 }
2201
2202 /// Lists the clips that contain the specified note.
2203 #[cfg(feature = "12-58-0")]
2204 #[cfg_attr(docsrs, doc(cfg(feature = "12-58-0")))]
2205 fn note_clips(
2206 &self,
2207 note: impl EntityRef<Note>,
2208 ) -> BoxFuture<Result<Vec<Clip>, Error<Self::Error>>> {
2209 let note_id = note.entity_ref();
2210 Box::pin(async move {
2211 let clips = self
2212 .request(endpoint::notes::clips::Request { note_id })
2213 .await
2214 .map_err(Error::Client)?
2215 .into_result()?;
2216 Ok(clips)
2217 })
2218 }
2219
2220 /// Clips the specified note.
2221 #[cfg(feature = "12-57-0")]
2222 #[cfg_attr(docsrs, doc(cfg(feature = "12-57-0")))]
2223 fn clip_note(
2224 &self,
2225 clip: impl EntityRef<Clip>,
2226 note: impl EntityRef<Note>,
2227 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2228 let clip_id = clip.entity_ref();
2229 let note_id = note.entity_ref();
2230 Box::pin(async move {
2231 self.request(endpoint::clips::add_note::Request { clip_id, note_id })
2232 .await
2233 .map_err(Error::Client)?
2234 .into_result()?;
2235 Ok(())
2236 })
2237 }
2238
2239 /// Lists the notes that are clipped to the specified clip.
2240 fn clip_notes(&self, clip: impl EntityRef<Clip>) -> PagerStream<BoxPager<Self, Note>> {
2241 let pager = BackwardPager::new(
2242 self,
2243 endpoint::clips::notes::Request::builder()
2244 .clip_id(clip.entity_ref())
2245 .build(),
2246 );
2247 PagerStream::new(Box::pin(pager))
2248 }
2249
2250 /// Gets the corresponding clip from the ID.
2251 fn get_clip(&self, id: Id<Clip>) -> BoxFuture<Result<Clip, Error<Self::Error>>> {
2252 Box::pin(async move {
2253 let clip = self
2254 .request(endpoint::clips::show::Request { clip_id: id })
2255 .await
2256 .map_err(Error::Client)?
2257 .into_result()?;
2258 Ok(clip)
2259 })
2260 }
2261
2262 /// Updates the name of the specified clip to the given one.
2263 #[cfg(not(feature = "12-57-0"))]
2264 #[cfg_attr(docsrs, doc(cfg(not(feature = "12-57-0"))))]
2265 fn rename_clip(
2266 &self,
2267 clip: impl EntityRef<Clip>,
2268 name: impl Into<String>,
2269 ) -> BoxFuture<Result<Clip, Error<Self::Error>>> {
2270 let clip_id = clip.entity_ref();
2271 let name = name.into();
2272 Box::pin(async move {
2273 let clip = self
2274 .request(endpoint::clips::update::Request { clip_id, name })
2275 .await
2276 .map_err(Error::Client)?
2277 .into_result()?;
2278 Ok(clip)
2279 })
2280 }
2281
2282 /// Updates the specified clip.
2283 ///
2284 /// This method actually returns a builder, namely [`ClipUpdateBuilder`].
2285 /// You can chain the method calls to it corresponding to the fields you want to update.
2286 /// Finally, calling [`update`][builder_update] method will actually perform the update.
2287 /// See [`ClipUpdateBuilder`] for the fields that can be updated.
2288 ///
2289 /// [builder_update]: ClipUpdateBuilder::update
2290 ///
2291 /// # Examples
2292 ///
2293 /// ```
2294 /// # use misskey_util::ClientExt;
2295 /// # #[tokio::main]
2296 /// # async fn main() -> anyhow::Result<()> {
2297 /// # let client = misskey_test::test_client().await?;
2298 /// let clip = client.create_clip("kawaii notes").await?;
2299 /// // Update the description and publish it.
2300 /// client
2301 /// .update_clip(clip)
2302 /// .public(true)
2303 /// .description("collection of kawaii notes")
2304 /// .update()
2305 /// .await?;
2306 /// # Ok(())
2307 /// # }
2308 /// ```
2309 #[cfg(feature = "12-57-0")]
2310 #[cfg_attr(docsrs, doc(cfg(feature = "12-57-0")))]
2311 fn update_clip(&self, clip: Clip) -> ClipUpdateBuilder<&Self> {
2312 ClipUpdateBuilder::new(self, clip)
2313 }
2314
2315 /// Lists the clips created by the specified user.
2316 #[cfg(feature = "12-61-0")]
2317 #[cfg_attr(docsrs, doc(cfg(feature = "12-61-0")))]
2318 fn user_clips(&self, user: impl EntityRef<User>) -> PagerStream<BoxPager<Self, Clip>> {
2319 let pager = BackwardPager::new(
2320 self,
2321 endpoint::users::clips::Request::builder()
2322 .user_id(user.entity_ref())
2323 .build(),
2324 );
2325 PagerStream::new(Box::pin(pager))
2326 }
2327 // }}}
2328
2329 // {{{ Messaging
2330 /// Sends a message to the user with the given text.
2331 fn create_message(
2332 &self,
2333 recipient: impl EntityRef<User>,
2334 text: impl Into<String>,
2335 ) -> BoxFuture<Result<MessagingMessage, Error<Self::Error>>> {
2336 let recipient = recipient.entity_ref();
2337 let text = text.into();
2338 Box::pin(async move {
2339 self.build_message()
2340 .user(recipient)
2341 .text(text)
2342 .create()
2343 .await
2344 })
2345 }
2346
2347 /// Sends a message to the user group with the given text.
2348 fn create_group_message(
2349 &self,
2350 recipient: impl EntityRef<UserGroup>,
2351 text: impl Into<String>,
2352 ) -> BoxFuture<Result<MessagingMessage, Error<Self::Error>>> {
2353 let recipient = recipient.entity_ref();
2354 let text = text.into();
2355 Box::pin(async move {
2356 self.build_message()
2357 .group(recipient)
2358 .text(text)
2359 .create()
2360 .await
2361 })
2362 }
2363
2364 /// Returns a builder for creating a message.
2365 ///
2366 /// The returned builder provides methods to customize details of the message and its recipients,
2367 /// and you can chain them to create a message incrementally.
2368 /// Finally, calling [`create`][builder_create] method will actually create and send a message.
2369 /// See [`MessagingMessageBuilder`] for the provided methods.
2370 ///
2371 /// [builder_create]: MessagingMessageBuilder::create
2372 fn build_message(&self) -> MessagingMessageBuilder<&Self> {
2373 MessagingMessageBuilder::new(self)
2374 }
2375
2376 /// Deletes the specified message.
2377 fn delete_message(
2378 &self,
2379 message: impl EntityRef<MessagingMessage>,
2380 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2381 let message_id = message.entity_ref();
2382 Box::pin(async move {
2383 self.request(endpoint::messaging::messages::delete::Request { message_id })
2384 .await
2385 .map_err(Error::Client)?
2386 .into_result()?;
2387 Ok(())
2388 })
2389 }
2390
2391 /// Marks the specified message as read.
2392 fn read_message(
2393 &self,
2394 message: impl EntityRef<MessagingMessage>,
2395 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2396 let message_id = message.entity_ref();
2397 Box::pin(async move {
2398 self.request(endpoint::messaging::messages::read::Request { message_id })
2399 .await
2400 .map_err(Error::Client)?
2401 .into_result()?;
2402 Ok(())
2403 })
2404 }
2405
2406 /// Lists the messages with the specified user.
2407 fn user_messages(
2408 &self,
2409 user: impl EntityRef<User>,
2410 ) -> PagerStream<BoxPager<Self, MessagingMessage>> {
2411 let pager = BackwardPager::new(
2412 self,
2413 endpoint::messaging::messages::Request::builder()
2414 .mark_as_read(false)
2415 .user_id(user.entity_ref())
2416 .build(),
2417 );
2418 PagerStream::new(Box::pin(pager))
2419 }
2420
2421 /// Lists the messages in the specified user group.
2422 fn group_messages(
2423 &self,
2424 group: impl EntityRef<UserGroup>,
2425 ) -> PagerStream<BoxPager<Self, MessagingMessage>> {
2426 let pager = BackwardPager::new(
2427 self,
2428 endpoint::messaging::messages::Request::builder()
2429 .mark_as_read(false)
2430 .group_id(group.entity_ref())
2431 .build(),
2432 );
2433 PagerStream::new(Box::pin(pager))
2434 }
2435
2436 /// Gets message logs for the user who is logged in with this client.
2437 fn messaging_history(&self) -> BoxFuture<Result<Vec<MessagingMessage>, Error<Self::Error>>> {
2438 Box::pin(async move {
2439 let mut messages = self
2440 .request(endpoint::messaging::history::Request {
2441 group: Some(false),
2442 limit: None,
2443 })
2444 .await
2445 .map_err(Error::Client)?
2446 .into_result()?;
2447 let group_messages = self
2448 .request(endpoint::messaging::history::Request {
2449 group: Some(true),
2450 limit: None,
2451 })
2452 .await
2453 .map_err(Error::Client)?
2454 .into_result()?;
2455 messages.extend(group_messages);
2456 Ok(messages)
2457 })
2458 }
2459 // }}}
2460
2461 // {{{ Drive
2462 /// Uploads the file from the given url to the drive.
2463 ///
2464 /// The difference between [`upload_file_from_url_`][alt] and this method is that the former
2465 /// can get the [`DriveFile`][drive_file] of the uploaded file, while the latter cannot.
2466 /// If you want to obtain the [`DriveFile`] of an uploaded file in v12.48.0 or later, you can
2467 /// use [`DriveFileUrlBuilder::upload_and_wait`] or download the file once on the client side
2468 /// and the use [`UploadFileClientExt::upload_file`] to upload it.
2469 ///
2470 /// [alt]: ClientExt::upload_file_from_url_
2471 /// [drive_file]: misskey_api::model::drive::DriveFile
2472 #[cfg(feature = "12-48-0")]
2473 #[cfg_attr(docsrs, doc(cfg(feature = "12-48-0")))]
2474 fn upload_file_from_url(&self, url: Url) -> BoxFuture<Result<(), Error<Self::Error>>> {
2475 Box::pin(async move { self.build_file_from_url(url).upload().await })
2476 }
2477
2478 /// Uploads the file from the given url to the drive.
2479 ///
2480 /// See [`upload_file_from_url`][alt] for the difference between them.
2481 ///
2482 /// [alt]: ClientExt::upload_file_from_url
2483 #[cfg(any(docsrs, not(feature = "12-48-0")))]
2484 #[cfg_attr(docsrs, doc(cfg(not(feature = "12-48-0"))))]
2485 fn upload_file_from_url_(&self, url: Url) -> BoxFuture<Result<DriveFile, Error<Self::Error>>> {
2486 Box::pin(async move { self.build_file_from_url(url).upload_().await })
2487 }
2488
2489 /// Returns a builder for creating a file on the drive.
2490 ///
2491 /// The returned builder provides methods to customize details of the file,
2492 /// and you can chain them to create a file incrementally.
2493 /// See [`DriveFileUrlBuilder`] for the provided methods.
2494 fn build_file_from_url(&self, url: Url) -> DriveFileUrlBuilder<&Self> {
2495 DriveFileUrlBuilder::with_url(self, url)
2496 }
2497
2498 /// Deletes the specified file on the drive.
2499 fn delete_file(
2500 &self,
2501 file: impl EntityRef<DriveFile>,
2502 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2503 let file_id = file.entity_ref();
2504 Box::pin(async move {
2505 self.request(endpoint::drive::files::delete::Request { file_id })
2506 .await
2507 .map_err(Error::Client)?
2508 .into_result()?;
2509 Ok(())
2510 })
2511 }
2512
2513 /// Updates the specified file
2514 ///
2515 /// This method actually returns a builder, namely [`DriveFileUpdateBuilder`].
2516 /// You can chain the method calls to it corresponding to the fields you want to update.
2517 /// Finally, calling [`update`][builder_update] method will actually perform the update.
2518 /// See [`DriveFileUpdateBuilder`] for the fields that can be updated.
2519 ///
2520 /// [builder_update]: DriveFileUpdateBuilder::update
2521 fn update_file(&self, file: impl EntityRef<DriveFile>) -> DriveFileUpdateBuilder<&Self> {
2522 DriveFileUpdateBuilder::new(self, file)
2523 }
2524
2525 /// Gets the corresponding file from the ID.
2526 fn get_file(&self, id: Id<DriveFile>) -> BoxFuture<Result<DriveFile, Error<Self::Error>>> {
2527 Box::pin(async move {
2528 let file = self
2529 .request(endpoint::drive::files::show::Request {
2530 file_id: Some(id),
2531 url: None,
2532 })
2533 .await
2534 .map_err(Error::Client)?
2535 .into_result()?;
2536 Ok(file)
2537 })
2538 }
2539
2540 /// Creates a folder on the drive with the given name.
2541 ///
2542 /// # Examples
2543 ///
2544 /// ```
2545 /// # use misskey_util::ClientExt;
2546 /// # #[tokio::main]
2547 /// # async fn main() -> anyhow::Result<()> {
2548 /// # let client = misskey_test::test_client().await?;
2549 /// let folder = client.create_folder("Folder1").await?;
2550 /// assert_eq!(folder.name, "Folder1");
2551 /// # Ok(())
2552 /// # }
2553 /// ```
2554 fn create_folder(
2555 &self,
2556 name: impl Into<String>,
2557 ) -> BoxFuture<Result<DriveFolder, Error<Self::Error>>> {
2558 let name = name.into();
2559 Box::pin(async move {
2560 let folder = self
2561 .request(endpoint::drive::folders::create::Request {
2562 name: Some(name),
2563 parent_id: None,
2564 })
2565 .await
2566 .map_err(Error::Client)?
2567 .into_result()?;
2568 Ok(folder)
2569 })
2570 }
2571
2572 /// Creates a folder on the drive with the given name and parent folder.
2573 fn create_folder_with_parent(
2574 &self,
2575 name: impl Into<String>,
2576 parent: impl EntityRef<DriveFolder>,
2577 ) -> BoxFuture<Result<DriveFolder, Error<Self::Error>>> {
2578 let name = name.into();
2579 let parent_id = parent.entity_ref();
2580 Box::pin(async move {
2581 let folder = self
2582 .request(endpoint::drive::folders::create::Request {
2583 name: Some(name),
2584 parent_id: Some(parent_id),
2585 })
2586 .await
2587 .map_err(Error::Client)?
2588 .into_result()?;
2589 Ok(folder)
2590 })
2591 }
2592
2593 /// Deletes the specified folder on the drive.
2594 fn delete_folder(
2595 &self,
2596 folder: impl EntityRef<DriveFolder>,
2597 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2598 let folder_id = folder.entity_ref();
2599 Box::pin(async move {
2600 self.request(endpoint::drive::folders::delete::Request { folder_id })
2601 .await
2602 .map_err(Error::Client)?
2603 .into_result()?;
2604 Ok(())
2605 })
2606 }
2607
2608 /// Updates the specified folder.
2609 ///
2610 /// This method actually returns a builder, namely [`DriveFolderUpdateBuilder`].
2611 /// You can chain the method calls to it corresponding to the fields you want to update.
2612 /// Finally, calling [`update`][builder_update] method will actually perform the update.
2613 /// See [`DriveFolderUpdateBuilder`] for the fields that can be updated.
2614 ///
2615 /// [builder_update]: DriveFolderUpdateBuilder::update
2616 ///
2617 /// # Examples
2618 ///
2619 /// ```
2620 /// # use misskey_util::ClientExt;
2621 /// # #[tokio::main]
2622 /// # async fn main() -> anyhow::Result<()> {
2623 /// # let client = misskey_test::test_client().await?;
2624 /// let folder = client.create_folder("Folder1").await?;
2625 /// client
2626 /// .update_folder(&folder)
2627 /// .name("Folder2")
2628 /// .update()
2629 /// .await?;
2630 /// # Ok(())
2631 /// # }
2632 /// ```
2633 fn update_folder(
2634 &self,
2635 folder: impl EntityRef<DriveFolder>,
2636 ) -> DriveFolderUpdateBuilder<&Self> {
2637 DriveFolderUpdateBuilder::new(self, folder)
2638 }
2639
2640 /// Gets the corresponding folder from the ID.
2641 fn get_folder(
2642 &self,
2643 id: Id<DriveFolder>,
2644 ) -> BoxFuture<Result<DriveFolder, Error<Self::Error>>> {
2645 Box::pin(async move {
2646 let folder = self
2647 .request(endpoint::drive::folders::show::Request { folder_id: id })
2648 .await
2649 .map_err(Error::Client)?
2650 .into_result()?;
2651 Ok(folder)
2652 })
2653 }
2654
2655 /// Lists the notes that have the specified file attached.
2656 fn attached_notes(
2657 &self,
2658 file: impl EntityRef<DriveFile>,
2659 ) -> BoxFuture<Result<Vec<Note>, Error<Self::Error>>> {
2660 let file_id = file.entity_ref();
2661 Box::pin(async move {
2662 let notes = self
2663 .request(endpoint::drive::files::attached_notes::Request { file_id })
2664 .await
2665 .map_err(Error::Client)?
2666 .into_result()?;
2667 Ok(notes)
2668 })
2669 }
2670
2671 /// Lists the files with the specified name.
2672 fn find_file_by_name(
2673 &self,
2674 name: impl Into<String>,
2675 ) -> BoxFuture<Result<Vec<DriveFile>, Error<Self::Error>>> {
2676 let name = name.into();
2677 Box::pin(async move {
2678 let files = self
2679 .request(endpoint::drive::files::find::Request {
2680 name,
2681 folder_id: None,
2682 })
2683 .await
2684 .map_err(Error::Client)?
2685 .into_result()?;
2686 Ok(files)
2687 })
2688 }
2689
2690 /// Lists the files with the specified name in the folder.
2691 fn find_file_by_name_in_folder(
2692 &self,
2693 name: impl Into<String>,
2694 folder: impl EntityRef<DriveFolder>,
2695 ) -> BoxFuture<Result<Vec<DriveFile>, Error<Self::Error>>> {
2696 let name = name.into();
2697 let folder_id = folder.entity_ref();
2698 Box::pin(async move {
2699 let files = self
2700 .request(endpoint::drive::files::find::Request {
2701 name,
2702 folder_id: Some(folder_id),
2703 })
2704 .await
2705 .map_err(Error::Client)?
2706 .into_result()?;
2707 Ok(files)
2708 })
2709 }
2710
2711 /// Lists the folders with the specified name.
2712 fn find_folder_by_name(
2713 &self,
2714 name: impl Into<String>,
2715 ) -> BoxFuture<Result<Vec<DriveFolder>, Error<Self::Error>>> {
2716 let name = name.into();
2717 Box::pin(async move {
2718 let files = self
2719 .request(endpoint::drive::folders::find::Request {
2720 name,
2721 parent_id: None,
2722 })
2723 .await
2724 .map_err(Error::Client)?
2725 .into_result()?;
2726 Ok(files)
2727 })
2728 }
2729
2730 /// Lists the folders with the specified name in the folder.
2731 fn find_folder_by_name_in_folder(
2732 &self,
2733 name: impl Into<String>,
2734 folder: impl EntityRef<DriveFolder>,
2735 ) -> BoxFuture<Result<Vec<DriveFolder>, Error<Self::Error>>> {
2736 let name = name.into();
2737 let folder_id = folder.entity_ref();
2738 Box::pin(async move {
2739 let files = self
2740 .request(endpoint::drive::folders::find::Request {
2741 name,
2742 parent_id: Some(folder_id),
2743 })
2744 .await
2745 .map_err(Error::Client)?
2746 .into_result()?;
2747 Ok(files)
2748 })
2749 }
2750
2751 /// Lists the files on the drive.
2752 ///
2753 /// This method actually returns a builder, namely [`DriveFileListBuilder`].
2754 /// You can specify how you want to list files by chaining methods.
2755 /// The [`list`][builder_list] method of the builder returns a [`Stream`][stream]
2756 /// that lists files in the specified way.
2757 ///
2758 /// [builder_list]: DriveFileListBuilder::list
2759 /// [stream]: futures::stream::Stream
2760 ///
2761 /// # Examples
2762 ///
2763 /// ```
2764 /// # use misskey_util::ClientExt;
2765 /// # #[tokio::main]
2766 /// # async fn main() -> anyhow::Result<()> {
2767 /// # let client = misskey_test::test_client().await?;
2768 /// # use misskey_api as misskey;
2769 /// use futures::stream::TryStreamExt;
2770 /// use mime::IMAGE_STAR;
2771 /// use misskey::model::drive::DriveFile;
2772 ///
2773 /// // Get a list of image files
2774 /// let images: Vec<DriveFile> = client
2775 /// .files()
2776 /// .type_(IMAGE_STAR)
2777 /// .list()
2778 /// .try_collect()
2779 /// .await?;
2780 /// # Ok(())
2781 /// # }
2782 /// ```
2783 fn files(&self) -> DriveFileListBuilder<&Self> {
2784 DriveFileListBuilder::new(self)
2785 }
2786
2787 /// Lists the folders.
2788 fn folders(&self) -> PagerStream<BoxPager<Self, DriveFolder>> {
2789 let pager = BackwardPager::new(self, endpoint::drive::folders::Request::default());
2790 PagerStream::new(Box::pin(pager))
2791 }
2792
2793 /// Lists the folders in the folder.
2794 fn folders_in_folder(
2795 &self,
2796 folder: impl EntityRef<DriveFolder>,
2797 ) -> PagerStream<BoxPager<Self, DriveFolder>> {
2798 let pager = BackwardPager::new(
2799 self,
2800 endpoint::drive::folders::Request {
2801 folder_id: Some(folder.entity_ref()),
2802 ..Default::default()
2803 },
2804 );
2805 PagerStream::new(Box::pin(pager))
2806 }
2807 // }}}
2808
2809 // {{{ Admin
2810 /// Sets moderator privileges for the specified user.
2811 ///
2812 /// This operation may require this client to be logged in with an admin account.
2813 fn add_moderator(
2814 &self,
2815 user: impl EntityRef<User>,
2816 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2817 let user_id = user.entity_ref();
2818 Box::pin(async move {
2819 self.request(endpoint::admin::moderators::add::Request { user_id })
2820 .await
2821 .map_err(Error::Client)?
2822 .into_result()?;
2823 Ok(())
2824 })
2825 }
2826
2827 /// Removes moderator privileges for the specified user.
2828 ///
2829 /// This operation may require this client to be logged in with an admin account.
2830 fn remove_moderator(
2831 &self,
2832 user: impl EntityRef<User>,
2833 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2834 let user_id = user.entity_ref();
2835 Box::pin(async move {
2836 self.request(endpoint::admin::moderators::remove::Request { user_id })
2837 .await
2838 .map_err(Error::Client)?
2839 .into_result()?;
2840 Ok(())
2841 })
2842 }
2843
2844 /// Promotes the specified note until the time.
2845 ///
2846 /// This operation may require moderator privileges.
2847 #[cfg(feature = "12-13-0")]
2848 #[cfg_attr(docsrs, doc(cfg(feature = "12-13-0")))]
2849 fn promote_note(
2850 &self,
2851 note: impl EntityRef<Note>,
2852 expires_at: DateTime<Utc>,
2853 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2854 let note_id = note.entity_ref();
2855 Box::pin(async move {
2856 self.request(endpoint::admin::promo::create::Request {
2857 note_id,
2858 expires_at,
2859 })
2860 .await
2861 .map_err(Error::Client)?
2862 .into_result()?;
2863 Ok(())
2864 })
2865 }
2866
2867 /// Lists the abuse user reports.
2868 ///
2869 /// This operation may require moderator privileges.
2870 fn abuse_user_reports(&self) -> PagerStream<BoxPager<Self, AbuseUserReport>> {
2871 let pager = BackwardPager::new(
2872 self,
2873 endpoint::admin::abuse_user_reports::Request::default(),
2874 );
2875 PagerStream::new(Box::pin(pager))
2876 }
2877
2878 /// Removes the specified abuse user report.
2879 ///
2880 /// This operation may require moderator privileges.
2881 #[cfg(any(docsrs, not(feature = "12-49-0")))]
2882 #[cfg_attr(docsrs, doc(cfg(not(feature = "12-49-0"))))]
2883 fn remove_abuse_user_report(
2884 &self,
2885 report: impl EntityRef<AbuseUserReport>,
2886 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2887 let report_id = report.entity_ref();
2888 Box::pin(async move {
2889 self.request(endpoint::admin::remove_abuse_user_report::Request { report_id })
2890 .await
2891 .map_err(Error::Client)?
2892 .into_result()?;
2893 Ok(())
2894 })
2895 }
2896
2897 /// Marks the specified abuse user report as resolved.
2898 ///
2899 /// This operation may require moderator privileges.
2900 #[cfg(feature = "12-49-0")]
2901 #[cfg_attr(docsrs, doc(cfg(feature = "12-49-0")))]
2902 fn resolve_abuse_user_report(
2903 &self,
2904 report: impl EntityRef<AbuseUserReport>,
2905 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
2906 let report_id = report.entity_ref();
2907 Box::pin(async move {
2908 self.request(endpoint::admin::resolve_abuse_user_report::Request { report_id })
2909 .await
2910 .map_err(Error::Client)?
2911 .into_result()?;
2912 Ok(())
2913 })
2914 }
2915
2916 /// Lists the server logs in the instance.
2917 ///
2918 /// This method actually returns a builder, namely [`ServerLogListBuilder`].
2919 /// You can specify how you want to list users by chaining methods.
2920 /// The [`list`][builder_list] method of the builder fetches the actual logs.
2921 ///
2922 /// This operation may require moderator privileges.
2923 ///
2924 /// [builder_list]: ServerLogListBuilder::list
2925 ///
2926 /// # Examples
2927 ///
2928 /// ```
2929 /// # use misskey_util::ClientExt;
2930 /// # #[tokio::main]
2931 /// # async fn main() -> anyhow::Result<()> {
2932 /// # let client = misskey_test::test_client().await?;
2933 /// # use misskey_api as misskey;
2934 /// // Get a first 10 entries of 'info' logs with 'chart' domain
2935 /// let logs = client
2936 /// .server_logs()
2937 /// .take(10)
2938 /// .info()
2939 /// .with_domain("chart")
2940 /// .list()
2941 /// .await?;
2942 /// # Ok(())
2943 /// # }
2944 /// ```
2945 fn server_logs(&self) -> ServerLogListBuilder<&Self> {
2946 ServerLogListBuilder::new(self)
2947 }
2948
2949 /// Lists the moderation logs in the instance.
2950 ///
2951 /// This operation may require moderator privileges.
2952 fn moderation_logs(&self) -> PagerStream<BoxPager<Self, ModerationLog>> {
2953 let pager = BackwardPager::new(
2954 self,
2955 endpoint::admin::show_moderation_logs::Request::default(),
2956 );
2957 PagerStream::new(Box::pin(pager))
2958 }
2959
2960 /// Silences the specified user.
2961 ///
2962 /// This operation may require moderator privileges.
2963 fn silence(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
2964 let user_id = user.entity_ref();
2965 Box::pin(async move {
2966 self.request(endpoint::admin::silence_user::Request { user_id })
2967 .await
2968 .map_err(Error::Client)?
2969 .into_result()?;
2970 Ok(())
2971 })
2972 }
2973
2974 /// Suspends the specified user.
2975 ///
2976 /// This operation may require moderator privileges.
2977 fn suspend(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
2978 let user_id = user.entity_ref();
2979 Box::pin(async move {
2980 self.request(endpoint::admin::suspend_user::Request { user_id })
2981 .await
2982 .map_err(Error::Client)?
2983 .into_result()?;
2984 Ok(())
2985 })
2986 }
2987
2988 /// Unsilences the specified user.
2989 ///
2990 /// This operation may require moderator privileges.
2991 fn unsilence(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
2992 let user_id = user.entity_ref();
2993 Box::pin(async move {
2994 self.request(endpoint::admin::unsilence_user::Request { user_id })
2995 .await
2996 .map_err(Error::Client)?
2997 .into_result()?;
2998 Ok(())
2999 })
3000 }
3001
3002 /// Unsuspends the specified user.
3003 ///
3004 /// This operation may require moderator privileges.
3005 fn unsuspend(&self, user: impl EntityRef<User>) -> BoxFuture<Result<(), Error<Self::Error>>> {
3006 let user_id = user.entity_ref();
3007 Box::pin(async move {
3008 self.request(endpoint::admin::unsuspend_user::Request { user_id })
3009 .await
3010 .map_err(Error::Client)?
3011 .into_result()?;
3012 Ok(())
3013 })
3014 }
3015
3016 /// Updates the instance information.
3017 ///
3018 /// This method actually returns a builder, namely [`MetaUpdateBuilder`].
3019 /// You can chain the method calls to it corresponding to the fields you want to update.
3020 /// Finally, calling [`update`][builder_update] method will actually perform the update.
3021 /// See [`MetaUpdateBuilder`] for the fields that can be updated.
3022 ///
3023 /// This operation may require this client to be logged in with an admin account.
3024 ///
3025 /// [builder_update]: MetaUpdateBuilder::update
3026 ///
3027 /// # Examples
3028 ///
3029 /// ```
3030 /// # use misskey_util::ClientExt;
3031 /// # #[tokio::main]
3032 /// # async fn main() -> anyhow::Result<()> {
3033 /// # let client = misskey_test::test_admin_client().await?;
3034 /// client
3035 /// .update_meta()
3036 /// .set_name("The Instance of Saturn")
3037 /// .max_note_text_length(5000)
3038 /// .update()
3039 /// .await?;
3040 /// # Ok(())
3041 /// # }
3042 /// ```
3043 fn update_meta(&self) -> MetaUpdateBuilder<&Self> {
3044 MetaUpdateBuilder::new(self)
3045 }
3046
3047 /// Creates an announcement with given title and text.
3048 ///
3049 /// This operation may require moderator privileges.
3050 fn create_announcement(
3051 &self,
3052 title: impl Into<String>,
3053 text: impl Into<String>,
3054 ) -> BoxFuture<Result<Announcement, Error<Self::Error>>> {
3055 let title = title.into();
3056 let text = text.into();
3057 Box::pin(async move {
3058 let announcement = self
3059 .request(endpoint::admin::announcements::create::Request {
3060 title,
3061 text,
3062 image_url: None,
3063 })
3064 .await
3065 .map_err(Error::Client)?
3066 .into_result()?;
3067 Ok(announcement)
3068 })
3069 }
3070
3071 /// Creates an announcement with given title, text, and image URL.
3072 ///
3073 /// This operation may require moderator privileges.
3074 fn create_announcement_with_image(
3075 &self,
3076 title: impl Into<String>,
3077 text: impl Into<String>,
3078 image_url: Url,
3079 ) -> BoxFuture<Result<Announcement, Error<Self::Error>>> {
3080 let title = title.into();
3081 let text = text.into();
3082 Box::pin(async move {
3083 let announcement = self
3084 .request(endpoint::admin::announcements::create::Request {
3085 title,
3086 text,
3087 image_url: Some(image_url),
3088 })
3089 .await
3090 .map_err(Error::Client)?
3091 .into_result()?;
3092 Ok(announcement)
3093 })
3094 }
3095
3096 /// Deletes the specified announcement.
3097 ///
3098 /// This operation may require moderator privileges.
3099 fn delete_announcement(
3100 &self,
3101 announcement: impl EntityRef<Announcement>,
3102 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
3103 let announcement_id = announcement.entity_ref();
3104 Box::pin(async move {
3105 self.request(endpoint::admin::announcements::delete::Request {
3106 id: announcement_id,
3107 })
3108 .await
3109 .map_err(Error::Client)?
3110 .into_result()?;
3111 Ok(())
3112 })
3113 }
3114
3115 /// Updates the specified announcement.
3116 ///
3117 /// This method actually returns a builder, namely [`AnnouncementUpdateBuilder`].
3118 /// You can chain the method calls to it corresponding to the fields you want to update.
3119 /// Finally, calling [`update`][builder_update] method will actually perform the update.
3120 /// See [`AnnouncementUpdateBuilder`] for the fields that can be updated.
3121 ///
3122 /// This operation may require moderator privileges.
3123 ///
3124 /// [builder_update]: AnnouncementUpdateBuilder::update
3125 fn update_announcement(&self, announcement: Announcement) -> AnnouncementUpdateBuilder<&Self> {
3126 AnnouncementUpdateBuilder::new(self, announcement)
3127 }
3128
3129 /// Creates a custom emoji from the given file.
3130 ///
3131 /// This operation may require moderator privileges.
3132 #[cfg(feature = "12-9-0")]
3133 #[cfg_attr(docsrs, doc(cfg(feature = "12-9-0")))]
3134 fn create_emoji(
3135 &self,
3136 file: impl EntityRef<DriveFile>,
3137 ) -> BoxFuture<Result<Id<Emoji>, Error<Self::Error>>> {
3138 let file_id = file.entity_ref();
3139 Box::pin(async move {
3140 let id = self
3141 .request(endpoint::admin::emoji::add::Request { file_id })
3142 .await
3143 .map_err(Error::Client)?
3144 .into_result()?
3145 .id;
3146 Ok(id)
3147 })
3148 }
3149
3150 /// Deletes the specified emoji.
3151 ///
3152 /// This operation may require moderator privileges.
3153 fn delete_emoji(
3154 &self,
3155 emoji: impl EntityRef<Emoji>,
3156 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
3157 let emoji_id = emoji.entity_ref();
3158 Box::pin(async move {
3159 self.request(endpoint::admin::emoji::remove::Request { id: emoji_id })
3160 .await
3161 .map_err(Error::Client)?
3162 .into_result()?;
3163 Ok(())
3164 })
3165 }
3166
3167 /// Updates the specified emoji.
3168 ///
3169 /// This method actually returns a builder, namely [`EmojiUpdateBuilder`].
3170 /// You can chain the method calls to it corresponding to the fields you want to update.
3171 /// Finally, calling [`update`][builder_update] method will actually perform the update.
3172 /// See [`EmojiUpdateBuilder`] for the fields that can be updated.
3173 ///
3174 /// This operation may require moderator privileges.
3175 ///
3176 /// [builder_update]: EmojiUpdateBuilder::update
3177 #[cfg(feature = "12-9-0")]
3178 #[cfg_attr(docsrs, doc(cfg(feature = "12-9-0")))]
3179 fn update_emoji(&self, emoji: Emoji) -> EmojiUpdateBuilder<&Self> {
3180 EmojiUpdateBuilder::new(self, emoji)
3181 }
3182
3183 /// Copies the specified emoji.
3184 ///
3185 /// This operation may require moderator privileges.
3186 fn copy_emoji(
3187 &self,
3188 emoji: impl EntityRef<Emoji>,
3189 ) -> BoxFuture<Result<Id<Emoji>, Error<Self::Error>>> {
3190 let emoji_id = emoji.entity_ref();
3191 Box::pin(async move {
3192 let id = self
3193 .request(endpoint::admin::emoji::copy::Request { emoji_id })
3194 .await
3195 .map_err(Error::Client)?
3196 .into_result()?
3197 .id;
3198 Ok(id)
3199 })
3200 }
3201
3202 /// Lists the emojis in the instance.
3203 ///
3204 /// This operation may require moderator privileges.
3205 /// Use [`meta`][`ClientExt::meta`] method if you want to get a list of custom emojis from normal users,
3206 fn emojis(&self) -> PagerStream<BoxPager<Self, Emoji>> {
3207 let pager = BackwardPager::new(self, endpoint::admin::emoji::list::Request::default());
3208 PagerStream::new(Box::pin(pager))
3209 }
3210
3211 /// Searches the emojis using the given query string.
3212 ///
3213 /// This operation may require moderator privileges.
3214 #[cfg(feature = "12-48-0")]
3215 fn search_emojis(&self, query: impl Into<String>) -> PagerStream<BoxPager<Self, Emoji>> {
3216 let pager = BackwardPager::new(
3217 self,
3218 endpoint::admin::emoji::list::Request {
3219 query: Some(query.into()),
3220 ..Default::default()
3221 },
3222 );
3223 PagerStream::new(Box::pin(pager))
3224 }
3225 // }}}
3226
3227 // {{{ Miscellaneous
3228 /// Gets information about the instance.
3229 fn meta(&self) -> BoxFuture<Result<Meta, Error<Self::Error>>> {
3230 Box::pin(async move {
3231 let meta = self
3232 .request(endpoint::meta::Request::default())
3233 .await
3234 .map_err(Error::Client)?
3235 .into_result()?;
3236 Ok(meta)
3237 })
3238 }
3239
3240 /// Lists announcements of the instance.
3241 fn announcements(&self) -> PagerStream<BoxPager<Self, Announcement>> {
3242 let pager = BackwardPager::new(self, endpoint::announcements::Request::default())
3243 .map_ok(|v| v.into_iter().map(|f| f.announcement).collect());
3244 PagerStream::new(Box::pin(pager))
3245 }
3246
3247 /// Lists the featured pages.
3248 #[cfg(feature = "12-58-0")]
3249 #[cfg_attr(docsrs, doc(cfg(feature = "12-58-0")))]
3250 fn featured_pages(&self) -> BoxFuture<Result<Vec<Page>, Error<Self::Error>>> {
3251 Box::pin(async move {
3252 let pages = self
3253 .request(endpoint::pages::featured::Request::default())
3254 .await
3255 .map_err(Error::Client)?
3256 .into_result()?;
3257 Ok(pages)
3258 })
3259 }
3260
3261 /// Lists the pages created by the specified user.
3262 #[cfg(feature = "12-61-0")]
3263 #[cfg_attr(docsrs, doc(cfg(feature = "12-61-0")))]
3264 fn user_pages(&self, user: impl EntityRef<User>) -> PagerStream<BoxPager<Self, Page>> {
3265 let pager = BackwardPager::new(
3266 self,
3267 endpoint::users::pages::Request::builder()
3268 .user_id(user.entity_ref())
3269 .build(),
3270 );
3271 PagerStream::new(Box::pin(pager))
3272 }
3273
3274 /// Marks all notifications as read.
3275 fn mark_all_notifications_as_read(&self) -> BoxFuture<Result<(), Error<Self::Error>>> {
3276 Box::pin(async move {
3277 self.request(endpoint::notifications::mark_all_as_read::Request::default())
3278 .await
3279 .map_err(Error::Client)?
3280 .into_result()?;
3281 Ok(())
3282 })
3283 }
3284
3285 /// Creates a notification with the given text.
3286 #[cfg(feature = "12-27-0")]
3287 #[cfg_attr(docsrs, doc(cfg(feature = "12-27-0")))]
3288 fn create_notification(
3289 &self,
3290 body: impl Into<String>,
3291 ) -> BoxFuture<Result<(), Error<Self::Error>>> {
3292 let body = body.into();
3293 Box::pin(async move { self.build_notification().body(body).create().await })
3294 }
3295
3296 /// Returns a builder for creating a notification.
3297 ///
3298 /// The returned builder provides methods to customize details of the notification,
3299 /// and you can chain them to create a notification incrementally.
3300 /// Finally, calling [`create`][builder_create] method will actually create a notification.
3301 /// See [`NotificationBuilder`] for the provided methods.
3302 ///
3303 /// [builder_create]: NotificationBuilder::create
3304 #[cfg(feature = "12-27-0")]
3305 #[cfg_attr(docsrs, doc(cfg(feature = "12-27-0")))]
3306 fn build_notification(&self) -> NotificationBuilder<&Self> {
3307 NotificationBuilder::new(self)
3308 }
3309 // }}}
3310}
3311
3312impl<C: Client + Sync> ClientExt for C {}
3313
3314/// An extension trait for [`UploadFileClient`][client] that provides convenient high-level APIs.
3315///
3316/// [client]: misskey_core::UploadFileClient
3317pub trait UploadFileClientExt: UploadFileClient + Sync {
3318 /// Uploads the file from the specified local path.
3319 fn upload_file(
3320 &self,
3321 path: impl AsRef<Path>,
3322 ) -> BoxFuture<Result<DriveFile, Error<Self::Error>>> {
3323 let path = path.as_ref().to_owned();
3324 Box::pin(async move { self.build_file(path).upload().await })
3325 }
3326
3327 /// Returns a builder for creating a file on the drive.
3328 ///
3329 /// The returned builder provides methods to customize details of the file,
3330 /// and you can chain them to create a file incrementally.
3331 /// See [`DriveFileBuilder`] for the provided methods.
3332 fn build_file(&self, path: impl AsRef<Path>) -> DriveFileBuilder<&Self> {
3333 DriveFileBuilder::with_path(self, path)
3334 }
3335}
3336
3337impl<C: UploadFileClient + Sync> UploadFileClientExt for C {}