use async_tungstenite::tungstenite::Message;
use futures::channel::mpsc::{TrySendError, UnboundedSender as Sender};
use super::{ChunkGuildFilter, ShardClientMessage, ShardRunnerMessage};
#[cfg(feature = "collector")]
use crate::collector::{
ComponentInteractionFilter,
EventFilter,
MessageFilter,
ModalInteractionFilter,
ReactionFilter,
};
use crate::gateway::InterMessage;
use crate::model::prelude::*;
/// A lightweight wrapper around an mpsc sender.
///
/// This is used to cleanly communicate with a shard's respective
/// [`ShardRunner`]. This can be used for actions such as setting the activity
/// via [`Self::set_activity`] or shutting down via [`Self::shutdown_clean`].
///
/// [`ShardRunner`]: super::ShardRunner
#[derive(Clone, Debug)]
pub struct ShardMessenger {
pub(crate) tx: Sender<InterMessage>,
}
impl ShardMessenger {
/// Creates a new shard messenger.
///
/// If you are using the [`Client`], you do not need to do this.
///
/// [`Client`]: crate::Client
#[inline]
#[must_use]
pub fn new(tx: Sender<InterMessage>) -> Self {
Self {
tx,
}
}
/// Requests that one or multiple [`Guild`]s be chunked.
///
/// This will ask the gateway to start sending member chunks for large
/// guilds (250 members+). If a guild is over 250 members, then a full
/// member list will not be downloaded, and must instead be requested to be
/// sent in "chunks" containing members.
///
/// Member chunks are sent as the [`Event::GuildMembersChunk`] event. Each
/// chunk only contains a partial amount of the total members.
///
/// If the `cache` feature is enabled, the cache will automatically be
/// updated with member chunks.
///
/// # Examples
///
/// Chunk a single guild by Id, limiting to 2000 [`Member`]s, and not
/// specifying a query parameter:
///
/// ```rust,no_run
/// # use tokio::sync::Mutex;
/// # use serenity::model::gateway::GatewayIntents;
/// # use serenity::client::bridge::gateway::ChunkGuildFilter;
/// # use serenity::gateway::Shard;
/// # use std::sync::Arc;
/// #
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let mutex = Arc::new(Mutex::new("".to_string()));
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", [0u64, 1u64],
/// # GatewayIntents::all()).await?;
/// #
/// use serenity::model::id::GuildId;
///
/// shard.chunk_guild(GuildId(81384788765712384), Some(2000), ChunkGuildFilter::None, None);
/// # Ok(())
/// # }
/// ```
///
/// Chunk a single guild by Id, limiting to 20 members, specifying a
/// query parameter of `"do"` and a nonce of `"request"`:
///
/// ```rust,no_run
/// # use tokio::sync::Mutex;
/// # use serenity::model::gateway::GatewayIntents;
/// # use serenity::client::bridge::gateway::ChunkGuildFilter;
/// # use serenity::gateway::Shard;
/// # use std::sync::Arc;
/// #
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let mutex = Arc::new(Mutex::new("".to_string()));
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", [0u64, 1u64],
/// # GatewayIntents::all()).await?;
/// #
/// use serenity::model::id::GuildId;
///
/// shard.chunk_guild(
/// GuildId(81384788765712384),
/// Some(20),
/// ChunkGuildFilter::Query("do".to_owned()),
/// Some("request"),
/// );
/// # Ok(())
/// # }
/// ```
pub fn chunk_guild(
&self,
guild_id: GuildId,
limit: Option<u16>,
filter: ChunkGuildFilter,
nonce: Option<String>,
) {
drop(self.send_to_shard(ShardRunnerMessage::ChunkGuild {
guild_id,
limit,
filter,
nonce,
}));
}
/// Sets the user's current activity, if any.
///
/// Other presence settings are maintained.
///
/// # Examples
///
/// Setting the current activity to playing `"Heroes of the Storm"`:
///
/// ```rust,no_run
/// # use tokio::sync::Mutex;
/// # use serenity::gateway::Shard;
/// # use serenity::model::gateway::GatewayIntents;
/// # use std::sync::Arc;
/// #
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let mutex = Arc::new(Mutex::new("".to_string()));
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", [0u64, 1u64],
/// # GatewayIntents::all()).await?;
/// use serenity::model::gateway::Activity;
///
/// shard.set_activity(Some(Activity::playing("Heroes of the Storm")));
/// # Ok(())
/// # }
/// ```
pub fn set_activity(&self, activity: Option<Activity>) {
drop(self.send_to_shard(ShardRunnerMessage::SetActivity(activity)));
}
/// Sets the user's full presence information.
///
/// Consider using the individual setters if you only need to modify one of
/// these.
///
/// # Examples
///
/// Set the current user as playing `"Heroes of the Storm"` and being
/// online:
///
/// ```rust,ignore
/// # use tokio::sync::Mutex;
/// # use serenity::gateway::Shard;
/// # use std::sync::Arc;
/// #
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let mutex = Arc::new(Mutex::new("".to_string()));
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", [0u64, 1u64], None).await?;
/// #
/// use serenity::model::gateway::Activity;
/// use serenity::model::user::OnlineStatus;
///
/// let activity = Activity::playing("Heroes of the Storm");
/// shard.set_presence(Some(activity), OnlineStatus::Online);
/// # Ok(())
/// # }
/// ```
pub fn set_presence(&self, activity: Option<Activity>, mut status: OnlineStatus) {
if status == OnlineStatus::Offline {
status = OnlineStatus::Invisible;
}
drop(self.send_to_shard(ShardRunnerMessage::SetPresence(status, activity)));
}
/// Sets the user's current online status.
///
/// Note that [`Offline`] is not a valid online status, so it is
/// automatically converted to [`Invisible`].
///
/// Other presence settings are maintained.
///
/// # Examples
///
/// Setting the current online status for the shard to [`DoNotDisturb`].
///
/// ```rust,no_run
/// # use tokio::sync::Mutex;
/// # use serenity::gateway::Shard;
/// # use serenity::model::gateway::GatewayIntents;
/// # use std::sync::Arc;
/// #
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// # let mutex = Arc::new(Mutex::new("".to_string()));
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", [0u64, 1u64],
/// # GatewayIntents::all()).await?;
/// #
/// use serenity::model::user::OnlineStatus;
///
/// shard.set_status(OnlineStatus::DoNotDisturb);
/// # Ok(())
/// # }
/// ```
///
/// [`DoNotDisturb`]: OnlineStatus::DoNotDisturb
/// [`Invisible`]: OnlineStatus::Invisible
/// [`Offline`]: OnlineStatus::Offline
pub fn set_status(&self, mut online_status: OnlineStatus) {
if online_status == OnlineStatus::Offline {
online_status = OnlineStatus::Invisible;
}
drop(self.send_to_shard(ShardRunnerMessage::SetStatus(online_status)));
}
/// Shuts down the websocket by attempting to cleanly close the
/// connection.
pub fn shutdown_clean(&self) {
drop(self.send_to_shard(ShardRunnerMessage::Close(1000, None)));
}
/// Sends a raw message over the WebSocket.
///
/// The given message is not mutated in any way, and is sent as-is.
///
/// You should only use this if you know what you're doing. If you're
/// wanting to, for example, send a presence update, prefer the usage of
/// the [`Self::set_presence`] method.
pub fn websocket_message(&self, message: Message) {
drop(self.send_to_shard(ShardRunnerMessage::Message(message)));
}
/// Sends a message to the shard.
///
/// # Errors
///
/// Returns a [`TrySendError`] if the shard's receiver was closed.
#[inline]
pub fn send_to_shard(&self, msg: ShardRunnerMessage) -> Result<(), TrySendError<InterMessage>> {
self.tx.unbounded_send(InterMessage::Client(Box::new(ShardClientMessage::Runner(msg))))
}
/// Sets a new filter for an event collector.
#[inline]
#[cfg(feature = "collector")]
pub fn set_event_filter(&self, collector: EventFilter) {
drop(self.send_to_shard(ShardRunnerMessage::SetEventFilter(collector)));
}
/// Sets a new filter for a message collector.
#[inline]
#[cfg(feature = "collector")]
pub fn set_message_filter(&self, collector: MessageFilter) {
drop(self.send_to_shard(ShardRunnerMessage::SetMessageFilter(collector)));
}
/// Sets a new filter for a reaction collector.
#[cfg(feature = "collector")]
pub fn set_reaction_filter(&self, collector: ReactionFilter) {
drop(self.send_to_shard(ShardRunnerMessage::SetReactionFilter(collector)));
}
/// Sets a new filter for a component interaction collector.
#[cfg(feature = "collector")]
pub fn set_component_interaction_filter(&self, collector: ComponentInteractionFilter) {
drop(self.send_to_shard(ShardRunnerMessage::SetComponentInteractionFilter(collector)));
}
/// Sets a new filter for a modal interaction collector.
#[cfg(feature = "collector")]
pub fn set_modal_interaction_filter(&self, collector: ModalInteractionFilter) {
drop(self.send_to_shard(ShardRunnerMessage::SetModalInteractionFilter(collector)));
}
}
impl AsRef<ShardMessenger> for ShardMessenger {
fn as_ref(&self) -> &ShardMessenger {
self
}
}