1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
use std::fmt::{Display, Formatter, Result as FmtResult};
#[cfg(feature = "model")]
use std::sync::Arc;
use chrono::{DateTime, Utc};
#[cfg(feature = "model")]
use crate::builder::{CreateMessage, EditMessage, GetMessages};
#[cfg(feature = "model")]
use crate::http::AttachmentType;
#[cfg(feature = "http")]
use crate::http::{Http, Typing};
use crate::model::prelude::*;
/// A Direct Message text channel with another user.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct PrivateChannel {
/// The unique Id of the private channel.
///
/// Can be used to calculate the first message's creation date.
pub id: ChannelId,
/// The Id of the last message sent.
pub last_message_id: Option<MessageId>,
/// Timestamp of the last time a [`Message`] was pinned.
pub last_pin_timestamp: Option<DateTime<Utc>>,
/// Indicator of the type of channel this is.
///
/// This should always be [`ChannelType::Private`].
#[serde(rename = "type")]
pub kind: ChannelType,
/// The recipient to the private channel.
#[serde(
deserialize_with = "deserialize_single_recipient",
serialize_with = "serialize_single_recipient",
rename = "recipients"
)]
pub recipient: User,
}
#[cfg(feature = "model")]
impl PrivateChannel {
/// Broadcasts that the current user is typing to the recipient.
///
/// See [ChannelId::broadcast_typing] for more details.
///
/// [`ChannelId::broadcast_typing`]: crate::model::channel::ChannelId::broadcast_typing
#[allow(clippy::missing_errors_doc)]
#[inline]
pub async fn broadcast_typing(&self, http: impl AsRef<Http>) -> Result<()> {
self.id.broadcast_typing(&http).await
}
/// React to a [`Message`] with a custom [`Emoji`] or unicode character.
///
/// [`Message::react`] may be a more suited method of reacting in most
/// cases.
///
/// # Errors
///
/// Returns [`Error::Http`] if the reaction cannot be added,
/// or if a message with that Id does not exist.
#[inline]
pub async fn create_reaction(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
reaction_type: impl Into<ReactionType>,
) -> Result<()> {
self.id.create_reaction(&http, message_id, reaction_type).await
}
/// Deletes the channel. This does not delete the contents of the channel,
/// and is equivalent to closing a private channel on the client, which can
/// be re-opened.
#[allow(clippy::missing_errors_doc)]
#[inline]
pub async fn delete(&self, http: impl AsRef<Http>) -> Result<Channel> {
self.id.delete(&http).await
}
/// Deletes all messages by Ids from the given vector in the channel.
///
/// The minimum amount of messages is 2 and the maximum amount is 100.
///
/// Requires the [Manage Messages] permission.
///
/// **Note**: Messages that are older than 2 weeks can't be deleted using
/// this method.
///
/// # Errors
///
/// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to
/// delete either 0 or more than 100 messages.
///
/// [Manage Messages]: Permissions::MANAGE_MESSAGES
#[inline]
pub async fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item = T>>(
&self,
http: impl AsRef<Http>,
message_ids: It,
) -> Result<()>
where
T: AsRef<MessageId>,
It: IntoIterator<Item = T>,
{
self.id.delete_messages(&http, message_ids).await
}
/// Deletes all permission overrides in the channel from a member
/// or role.
///
/// **Note**: Requires the [Manage Channel] permission.
///
/// [Manage Channel]: Permissions::MANAGE_CHANNELS
#[allow(clippy::missing_errors_doc)]
#[inline]
pub async fn delete_permission(
&self,
http: impl AsRef<Http>,
permission_type: PermissionOverwriteType,
) -> Result<()> {
self.id.delete_permission(&http, permission_type).await
}
/// Deletes the given [`Reaction`] from the channel.
///
/// **Note**: In private channels, the current user may only
/// delete it's own reactions.
///
/// # Errors
///
/// Returns [`Error::Http`] if the reaction is not from
/// the current user.
#[inline]
pub async fn delete_reaction(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
user_id: Option<UserId>,
reaction_type: impl Into<ReactionType>,
) -> Result<()> {
self.id.delete_reaction(&http, message_id, user_id, reaction_type).await
}
/// Edits a [`Message`] in the channel given its Id.
///
/// Message editing preserves all unchanged message data.
///
/// Refer to the documentation for [`EditMessage`] for more information
/// regarding message restrictions and requirements.
///
/// **Note**: Requires that the current user be the author of the message.
///
/// # Errors
///
/// Returns a [`ModelError::MessageTooLong`] if the content of the message
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
/// Returns [`Error::Http`] if the current user is not the owner of the message.
///
/// [`EditMessage`]: crate::builder::EditMessage
/// [`the limit`]: crate::builder::EditMessage::content
#[inline]
pub async fn edit_message<F>(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
f: F,
) -> Result<Message>
where
F: FnOnce(&mut EditMessage) -> &mut EditMessage,
{
self.id.edit_message(&http, message_id, f).await
}
/// Determines if the channel is NSFW.
///
/// **Note**: This method is for consistency. This will always return
/// `false`, due to DMs not being considered NSFW.
#[inline]
pub fn is_nsfw(&self) -> bool {
false
}
/// Gets a message from the channel.
///
/// # Errors
///
/// Returns [`Error::Http`] if a message with that Id does not
/// exist in this channel.
#[inline]
pub async fn message(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
) -> Result<Message> {
self.id.message(&http, message_id).await
}
/// Gets messages from the channel.
///
/// Refer to [`GetMessages`] for more information on how to use `builder`.
///
/// # Errors
///
/// Returns [`Error::Http`] if an invalid value is set in the builder.
///
/// [`GetMessages`]: crate::builder::GetMessages
#[inline]
pub async fn messages<F>(&self, http: impl AsRef<Http>, builder: F) -> Result<Vec<Message>>
where
F: FnOnce(&mut GetMessages) -> &mut GetMessages,
{
self.id.messages(&http, builder).await
}
/// Returns "DM with $username#discriminator".
pub fn name(&self) -> String {
format!("DM with {}", self.recipient.tag())
}
/// Gets the list of [`User`]s who have reacted to a [`Message`] with a
/// certain [`Emoji`].
///
/// The default `limit` is `50` - specify otherwise to receive a different
/// maximum number of users. The maximum that may be retrieve at a time is
/// `100`, if a greater number is provided then it is automatically reduced.
///
/// The optional `after` attribute is to retrieve the users after a certain
/// user. This is useful for pagination.
///
/// # Errors
///
/// Returns [`Error::Http`] if a message with the given Id does not exist
/// in the channel.
#[inline]
pub async fn reaction_users<M, R, U>(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
reaction_type: impl Into<ReactionType>,
limit: Option<u8>,
after: impl Into<Option<UserId>>,
) -> Result<Vec<User>> {
self.id.reaction_users(&http, message_id, reaction_type, limit, after).await
}
/// Pins a [`Message`] to the channel.
///
/// # Errors
///
/// Returns [`Error::Http`] if the number of pinned messages
/// would exceed the 50 message limit.
#[inline]
pub async fn pin(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
) -> Result<()> {
self.id.pin(&http, message_id).await
}
/// Retrieves the list of messages that have been pinned in the private
/// channel.
#[allow(clippy::missing_errors_doc)]
#[inline]
pub async fn pins(&self, http: impl AsRef<Http>) -> Result<Vec<Message>> {
self.id.pins(&http).await
}
/// Sends a message with just the given message content in the channel.
///
/// # Errors
///
/// Returns a [`ModelError::MessageTooLong`] if the content of the message
/// is over the above limit, containing the number of unicode code points
/// over the limit.
#[inline]
pub async fn say(
&self,
http: impl AsRef<Http>,
content: impl std::fmt::Display,
) -> Result<Message> {
self.id.say(&http, content).await
}
/// Sends (a) file(s) along with optional message contents.
///
/// Refer to [`ChannelId::send_files`] for examples and more information.
///
/// The [Attach Files] and [Send Messages] permissions are required.
///
/// **Note**: Message contents must be under 2000 unicode code points.
///
/// # Errors
///
/// If the content of the message is over the above limit, then a
/// [`ModelError::MessageTooLong`] will be returned, containing the number
/// of unicode code points over the limit.
///
/// [Send Messages]: Permissions::SEND_MESSAGES
#[inline]
pub async fn send_files<'a, F, T, It>(
&self,
http: impl AsRef<Http>,
files: It,
f: F,
) -> Result<Message>
where
for<'b> F: FnOnce(&'b mut CreateMessage<'a>) -> &'b mut CreateMessage<'a>,
T: Into<AttachmentType<'a>>,
It: IntoIterator<Item = T>,
{
self.id.send_files(&http, files, f).await
}
/// Sends a message to the channel with the given content.
///
/// Refer to the documentation for [`CreateMessage`] for more information
/// regarding message restrictions and requirements.
///
/// # Errors
///
/// Returns a [`ModelError::MessageTooLong`] if the content of the message
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
/// [`CreateMessage`]: crate::builder::CreateMessage
#[inline]
pub async fn send_message<'a, F>(&self, http: impl AsRef<Http>, f: F) -> Result<Message>
where
for<'b> F: FnOnce(&'b mut CreateMessage<'a>) -> &'b mut CreateMessage<'a>,
{
self.id.send_message(&http, f).await
}
/// Starts typing in the channel for an indefinite period of time.
///
/// Returns [`Typing`] that is used to trigger the typing. [`Typing::stop`] must be called
/// on the returned struct to stop typing. Note that on some clients, typing may persist
/// for a few seconds after [`Typing::stop`] is called.
/// Typing is also stopped when the struct is dropped.
///
/// If a message is sent while typing is triggered, the user will stop typing for a brief period
/// of time and then resume again until either [`Typing::stop`] is called or the struct is dropped.
///
/// This should rarely be used for bots, although it is a good indicator that a
/// long-running command is still being processed.
///
/// ## Examples
///
/// ```rust,no_run
/// # #[cfg(feature = "cache")]
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # use serenity::{
/// # cache::Cache,
/// # http::{Http, Typing},
/// # model::{ModelError, channel::PrivateChannel, id::ChannelId},
/// # Result,
/// # };
/// # use std::sync::Arc;
/// #
/// # fn long_process() {}
/// # let http = Arc::new(Http::default());
/// # let cache = Cache::default();
/// # let channel = cache.private_channel(ChannelId(7))
/// # .await
/// # .ok_or(ModelError::ItemMissing)?;
/// // Initiate typing (assuming http is `Arc<Http>` and `channel` is bound)
/// let typing = channel.start_typing(&http)?;
///
/// // Run some long-running process
/// long_process();
///
/// // Stop typing
/// typing.stop();
/// #
/// # Ok(())
/// # }
/// ```
///
/// # Errors
///
/// May return [`Error::Http`] if the current user cannot send a direct message
/// to this user.
pub fn start_typing(self, http: &Arc<Http>) -> Result<Typing> {
http.start_typing(self.id.0)
}
/// Unpins a [`Message`] in the channel given by its Id.
///
/// # Errors
///
/// Returns [`Error::Http`] if the current user lacks permission,
/// if the message was deleted, or if the channel already has the limit of
/// 50 pinned messages.
#[inline]
pub async fn unpin(
&self,
http: impl AsRef<Http>,
message_id: impl Into<MessageId>,
) -> Result<()> {
self.id.unpin(&http, message_id).await
}
}
impl Display for PrivateChannel {
/// Formats the private channel, displaying the recipient's username.
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str(&self.recipient.name)
}
}