Skip to main content

twilight_http/request/channel/thread/create_forum_thread/
mod.rs

1mod message;
2
3pub use self::message::CreateForumThreadMessage;
4
5use self::message::CreateForumThreadMessageFields;
6use crate::{
7    client::Client,
8    error::Error,
9    request::{self, AuditLogReason, Nullable, Request, attachment::AttachmentManager},
10    response::ResponseFuture,
11    routing::Route,
12};
13use serde::{Deserialize, Serialize};
14use twilight_model::{
15    channel::{Channel, Message, thread::AutoArchiveDuration},
16    id::{
17        Id,
18        marker::{ChannelMarker, TagMarker},
19    },
20};
21use twilight_validate::request::{ValidationError, audit_reason as validate_audit_reason};
22
23#[derive(Deserialize, Serialize)]
24pub struct ForumThread {
25    #[serde(flatten)]
26    pub channel: Channel,
27    pub message: Message,
28}
29
30#[derive(Serialize)]
31struct CreateForumThreadFields<'a> {
32    #[serde(skip_serializing_if = "Option::is_none")]
33    applied_tags: Option<&'a [Id<TagMarker>]>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    auto_archive_duration: Option<AutoArchiveDuration>,
36    message: CreateForumThreadMessageFields<'a>,
37    name: &'a str,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    rate_limit_per_user: Option<u16>,
40}
41
42/// Creates a new thread in a forum channel.
43///
44/// Requires the [`SEND_MESSAGES`] permission.
45///
46/// [`SEND_MESSAGES`]: twilight_model::guild::Permissions::SEND_MESSAGES
47#[must_use = "requests must be configured and executed"]
48pub struct CreateForumThread<'a> {
49    attachment_manager: AttachmentManager<'a>,
50    channel_id: Id<ChannelMarker>,
51    fields: CreateForumThreadFields<'a>,
52    http: &'a Client,
53    reason: Result<Option<&'a str>, ValidationError>,
54}
55
56impl<'a> CreateForumThread<'a> {
57    pub(crate) const fn new(
58        http: &'a Client,
59        channel_id: Id<ChannelMarker>,
60        name: &'a str,
61    ) -> Self {
62        Self {
63            attachment_manager: AttachmentManager::new(),
64            channel_id,
65            fields: CreateForumThreadFields {
66                applied_tags: None,
67                auto_archive_duration: None,
68                message: CreateForumThreadMessageFields {
69                    allowed_mentions: None,
70                    attachments: None,
71                    components: None,
72                    content: None,
73                    embeds: None,
74                    flags: None,
75                    payload_json: None,
76                    sticker_ids: None,
77                },
78                name,
79                rate_limit_per_user: None,
80            },
81            http,
82            reason: Ok(None),
83        }
84    }
85
86    /// Set the forum thread's applied tags.
87    pub const fn applied_tags(mut self, applied_tags: &'a [Id<TagMarker>]) -> Self {
88        self.fields.applied_tags = Some(applied_tags);
89
90        self
91    }
92
93    /// Set the default auto archive duration for newly created threads in the
94    /// channel.
95    ///
96    /// Automatic archive durations are not locked behind the guild's boost
97    /// level.
98    pub const fn auto_archive_duration(
99        mut self,
100        auto_archive_duration: AutoArchiveDuration,
101    ) -> Self {
102        self.fields.auto_archive_duration = Some(auto_archive_duration);
103
104        self
105    }
106
107    pub const fn message(self) -> CreateForumThreadMessage<'a> {
108        CreateForumThreadMessage::new(self)
109    }
110
111    /// Execute the request, returning a future resolving to a [`Response`].
112    ///
113    /// [`Response`]: crate::response::Response
114    fn exec(self) -> ResponseFuture<ForumThread> {
115        let http = self.http;
116
117        match self.try_into_request() {
118            Ok(request) => http.request(request),
119            Err(source) => ResponseFuture::error(source),
120        }
121    }
122
123    fn try_into_request(mut self) -> Result<Request, Error> {
124        let mut request = Request::builder(&Route::CreateForumThread {
125            channel_id: self.channel_id.get(),
126        });
127
128        if let Some(reason) = self.reason.map_err(Error::validation)? {
129            request = request.headers(request::audit_header(reason)?);
130        }
131
132        // Set the default allowed mentions if required.
133        if self.fields.message.allowed_mentions.is_none()
134            && let Some(allowed_mentions) = self.http.default_allowed_mentions()
135        {
136            self.fields.message.allowed_mentions = Some(Nullable(Some(allowed_mentions)));
137        }
138
139        // Determine whether we need to use a multipart/form-data body or a JSON
140        // body.
141        if !self.attachment_manager.is_empty() {
142            let form = if let Some(payload_json) = self.fields.message.payload_json {
143                self.attachment_manager.build_form(payload_json)
144            } else {
145                self.fields.message.attachments =
146                    Some(self.attachment_manager.get_partial_attachments());
147
148                let fields = crate::json::to_vec(&self.fields).map_err(Error::json)?;
149
150                self.attachment_manager.build_form(fields.as_ref())
151            };
152
153            request = request.form(form);
154        } else if let Some(payload_json) = self.fields.message.payload_json {
155            request = request.body(payload_json.to_vec());
156        } else {
157            request = request.json(&self.fields);
158        }
159
160        request.build()
161    }
162}
163
164impl<'a> AuditLogReason<'a> for CreateForumThread<'a> {
165    fn reason(mut self, reason: &'a str) -> Self {
166        self.reason = validate_audit_reason(reason).and(Ok(Some(reason)));
167
168        self
169    }
170}