Skip to main content

twilight_http/request/guild/ban/
bulk_ban.rs

1//! Builder and response body for the Bulk Ban request.
2
3use crate::{
4    client::Client,
5    error::Error,
6    request::{self, AuditLogReason, Request, TryIntoRequest},
7    response::{Response, ResponseFuture},
8    routing::Route,
9};
10use serde::{Deserialize, Serialize};
11use std::future::IntoFuture;
12use twilight_model::id::{
13    Id,
14    marker::{GuildMarker, UserMarker},
15};
16use twilight_validate::request::{
17    ValidationError, audit_reason as validate_audit_reason,
18    bulk_guild_ban_delete_message_seconds as validate_bulk_guild_ban_delete_message_seconds,
19    bulk_guild_ban_users as validate_bulk_guild_ban_users,
20};
21
22/// Response body for the [`BulkGuildBan`] request.
23#[derive(Clone, Debug, Deserialize, Serialize)]
24pub struct BulkGuildBanResponseBody {
25    /// List of user IDs that were successfully banned.
26    pub banned_users: Vec<Id<UserMarker>>,
27    /// List of user IDs that were not banned.
28    ///
29    /// A user ban may have failed for reasons including, but not limited to, a
30    /// server-side or the user already being banned.
31    pub failed_users: Vec<Id<UserMarker>>,
32}
33
34#[derive(Serialize)]
35struct BulkGuildBanFields<'a> {
36    /// Number of seconds to delete messages for, between `0` and `604800`.
37    #[serde(skip_serializing_if = "Option::is_none")]
38    delete_message_seconds: Option<u32>,
39    /// List of user IDs to ban from the guild.
40    user_ids: &'a [Id<UserMarker>],
41}
42
43/// Ban up to 200 users from a guild in bulk, optionally deleting their recent
44/// messages and specifying an Audit Log reason.
45///
46/// Requires both the [`BAN_MEMBERS`] and [`MANAGE_GUILD`] permissions.
47///
48/// # Examples
49///
50/// Ban a list of users from a guild along with the last day's messages:
51///
52/// ```no_run
53/// use twilight_http::{Client, request::AuditLogReason};
54/// use twilight_model::id::{Id, marker::UserMarker};
55///
56/// # #[tokio::main]
57/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
58/// # fn determine_users_to_ban() -> Vec<Id<UserMarker>> { Vec::new() }
59/// let client = Client::new("my token".to_owned());
60///
61/// let guild_id = Id::new(1);
62/// let user_ids: Vec<Id<UserMarker>> = determine_users_to_ban();
63/// client
64///     .bulk_guild_ban(guild_id, &user_ids)
65///     .delete_message_seconds(86_400)
66///     .reason("insulting my little pony")
67///     .await?;
68/// # Ok(()) }
69/// ```
70///
71/// [`BAN_MEMBERS`]: twilight_model::guild::Permissions::BAN_MEMBERS
72/// [`MANAGE_GUILD`]: twilight_model::guild::Permissions::MANAGE_GUILD
73#[must_use = "requests must be configured and executed"]
74pub struct BulkGuildBan<'a> {
75    fields: Result<BulkGuildBanFields<'a>, ValidationError>,
76    guild_id: Id<GuildMarker>,
77    http: &'a Client,
78    reason: Result<Option<&'a str>, ValidationError>,
79}
80
81impl<'a> BulkGuildBan<'a> {
82    pub(crate) fn new(
83        http: &'a Client,
84        guild_id: Id<GuildMarker>,
85        user_ids: &'a [Id<UserMarker>],
86    ) -> Self {
87        let fields = Ok(BulkGuildBanFields {
88            delete_message_seconds: None,
89            user_ids,
90        })
91        .and_then(|fields| {
92            validate_bulk_guild_ban_users(user_ids)?;
93
94            Ok(fields)
95        });
96
97        Self {
98            fields,
99            guild_id,
100            http,
101            reason: Ok(None),
102        }
103    }
104
105    /// Set the number of seconds' worth of messages to delete.
106    ///
107    /// The number of seconds must be less than or equal to `604_800` (this is equivalent to `7` days).
108    ///
109    /// Discord's defaults this to 0 seconds.
110    ///
111    /// # Errors
112    ///
113    /// Returns an error of type [`BulkGuildBanDeleteMessageSeconds`] if the
114    /// number of seconds is greater than `604_800` (this is equivalent to `7` days).
115    ///
116    /// [`BulkGuildBanDeleteMessageSeconds`]: twilight_validate::request::ValidationErrorType::BulkGuildBanDeleteMessageSeconds
117    pub fn delete_message_seconds(mut self, seconds: u32) -> Self {
118        self.fields = self.fields.and_then(|mut fields| {
119            validate_bulk_guild_ban_delete_message_seconds(seconds)?;
120            fields.delete_message_seconds = Some(seconds);
121
122            Ok(fields)
123        });
124
125        self
126    }
127}
128
129impl<'a> AuditLogReason<'a> for BulkGuildBan<'a> {
130    fn reason(mut self, reason: &'a str) -> Self {
131        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
132
133        self
134    }
135}
136
137impl IntoFuture for BulkGuildBan<'_> {
138    type Output = Result<Response<BulkGuildBanResponseBody>, Error>;
139
140    type IntoFuture = ResponseFuture<BulkGuildBanResponseBody>;
141
142    fn into_future(self) -> Self::IntoFuture {
143        let http = self.http;
144
145        match self.try_into_request() {
146            Ok(request) => http.request(request),
147            Err(source) => ResponseFuture::error(source),
148        }
149    }
150}
151
152impl TryIntoRequest for BulkGuildBan<'_> {
153    fn try_into_request(self) -> Result<Request, Error> {
154        let fields = self.fields.map_err(Error::validation)?;
155        let mut request = Request::builder(&Route::BulkGuildBan {
156            guild_id: self.guild_id.get(),
157        })
158        .json(&fields);
159
160        if let Some(reason) = self.reason.map_err(Error::validation)? {
161            request = request.headers(request::audit_header(reason)?);
162        }
163
164        request.build()
165    }
166}