use super::super::allowed_mentions::{AllowedMentions, AllowedMentionsBuilder, Unspecified};
use crate::request::{multipart::Form, prelude::*};
use std::{
collections::HashMap,
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::{embed::Embed, message::MessageReference, Message},
id::{ChannelId, MessageId},
};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum CreateMessageError {
ContentInvalid {
content: String,
},
EmbedTooLarge {
embed: Box<Embed>,
source: EmbedValidationError,
},
}
impl Display for CreateMessageError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::ContentInvalid { .. } => f.write_str("the message content is invalid"),
Self::EmbedTooLarge { .. } => f.write_str("the embed's contents are too long"),
}
}
}
impl Error for CreateMessageError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::ContentInvalid { .. } => None,
Self::EmbedTooLarge { source, .. } => Some(source),
}
}
}
#[derive(Default, Serialize)]
pub(crate) struct CreateMessageFields {
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
message_reference: Option<MessageReference>,
#[serde(skip_serializing_if = "Option::is_none")]
nonce: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
payload_json: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) allowed_mentions: Option<AllowedMentions>,
#[serde(skip_serializing_if = "Option::is_none")]
tts: Option<bool>,
}
pub struct CreateMessage<'a> {
attachments: HashMap<String, Vec<u8>>,
channel_id: ChannelId,
pub(crate) fields: CreateMessageFields,
fut: Option<Pending<'a, Message>>,
http: &'a Client,
}
impl<'a> CreateMessage<'a> {
pub(crate) fn new(http: &'a Client, channel_id: ChannelId) -> Self {
Self {
attachments: HashMap::new(),
channel_id,
fields: CreateMessageFields {
allowed_mentions: http.default_allowed_mentions(),
..CreateMessageFields::default()
},
fut: None,
http,
}
}
pub fn allowed_mentions(
self,
) -> AllowedMentionsBuilder<'a, Unspecified, Unspecified, Unspecified> {
AllowedMentionsBuilder::for_builder(self)
}
pub fn attachment(mut self, name: impl Into<String>, file: impl Into<Vec<u8>>) -> Self {
self.attachments.insert(name.into(), file.into());
self
}
pub fn attachments<N: Into<String>, F: Into<Vec<u8>>>(
mut self,
attachments: impl IntoIterator<Item = (N, F)>,
) -> Self {
for (name, file) in attachments {
self = self.attachment(name, file);
}
self
}
pub fn content(self, content: impl Into<String>) -> Result<Self, CreateMessageError> {
self._content(content.into())
}
fn _content(mut self, content: String) -> Result<Self, CreateMessageError> {
if !validate::content_limit(&content) {
return Err(CreateMessageError::ContentInvalid { content });
}
self.fields.content.replace(content);
Ok(self)
}
pub fn embed(mut self, embed: Embed) -> Result<Self, CreateMessageError> {
if let Err(source) = validate::embed(&embed) {
return Err(CreateMessageError::EmbedTooLarge {
embed: Box::new(embed),
source,
});
}
self.fields.embed.replace(embed);
Ok(self)
}
pub fn nonce(mut self, nonce: u64) -> Self {
self.fields.nonce.replace(nonce);
self
}
pub fn payload_json(mut self, payload_json: impl Into<Vec<u8>>) -> Self {
self.fields.payload_json.replace(payload_json.into());
self
}
pub fn reply(mut self, other: MessageId) -> Self {
self.fields.message_reference.replace(MessageReference {
channel_id: Some(self.channel_id),
guild_id: None,
message_id: Some(other),
});
self
}
pub fn tts(mut self, tts: bool) -> Self {
self.fields.tts.replace(tts);
self
}
fn start(&mut self) -> Result<()> {
self.fut.replace(Box::pin(self.http.request(
if self.attachments.is_empty() {
Request::from((
crate::json_to_vec(&self.fields)?,
Route::CreateMessage {
channel_id: self.channel_id.0,
},
))
} else {
let mut multipart = Form::new();
for (index, (name, file)) in self.attachments.drain().enumerate() {
multipart.file(format!("{}", index).as_bytes(), name.as_bytes(), &file);
}
let body = crate::json_to_vec(&self.fields)?;
multipart.part(b"payload_json", &body);
Request::from((
multipart,
Route::CreateMessage {
channel_id: self.channel_id.0,
},
))
},
)));
Ok(())
}
}
poll_req!(CreateMessage<'_>, Message);