serenity/gateway/bridge/
shard_messenger.rs

1#[cfg(feature = "collector")]
2use std::sync::Arc;
3
4use futures::channel::mpsc::UnboundedSender as Sender;
5use tokio_tungstenite::tungstenite::Message;
6
7#[cfg(feature = "collector")]
8use super::CollectorCallback;
9use super::{ChunkGuildFilter, ShardRunner, ShardRunnerMessage};
10use crate::gateway::ActivityData;
11use crate::model::prelude::*;
12
13/// A handle to a [`ShardRunner`].
14///
15/// This is used to cleanly communicate with a shard's respective [`ShardRunner`]. This can be used
16/// for actions such as setting the activity via [`Self::set_activity`] or shutting down via
17/// [`Self::shutdown_clean`].
18///
19/// [`ShardRunner`]: super::ShardRunner
20#[derive(Clone, Debug)]
21pub struct ShardMessenger {
22    pub(crate) tx: Sender<ShardRunnerMessage>,
23    #[cfg(feature = "collector")]
24    pub(crate) collectors: Arc<std::sync::Mutex<Vec<CollectorCallback>>>,
25}
26
27impl ShardMessenger {
28    /// Creates a new shard messenger.
29    ///
30    /// If you are using the [`Client`], you do not need to do this.
31    ///
32    /// [`Client`]: crate::Client
33    #[inline]
34    #[must_use]
35    pub fn new(shard: &ShardRunner) -> Self {
36        Self {
37            tx: shard.runner_tx(),
38            #[cfg(feature = "collector")]
39            collectors: Arc::clone(&shard.collectors),
40        }
41    }
42
43    /// Requests that one or multiple [`Guild`]s be chunked.
44    ///
45    /// This will ask the gateway to start sending member chunks for large guilds (250 members+).
46    /// If a guild is over 250 members, then a full member list will not be downloaded, and must
47    /// instead be requested to be sent in "chunks" containing members.
48    ///
49    /// Member chunks are sent as the [`Event::GuildMembersChunk`] event. Each chunk only contains
50    /// a partial amount of the total members.
51    ///
52    /// If the `cache` feature is enabled, the cache will automatically be updated with member
53    /// chunks.
54    ///
55    /// # Examples
56    ///
57    /// Chunk a single guild by Id, limiting to 2000 [`Member`]s, and not specifying a query
58    /// parameter:
59    ///
60    /// ```rust,no_run
61    /// # use tokio::sync::Mutex;
62    /// # use serenity::model::gateway::{GatewayIntents, ShardInfo};
63    /// # use serenity::model::id::ShardId;
64    /// # use serenity::gateway::{ChunkGuildFilter, Shard};
65    /// # use std::sync::Arc;
66    /// #
67    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
68    /// #     let mutex = Arc::new(Mutex::new("".to_string()));
69    /// #
70    /// #     let shard_info = ShardInfo {
71    /// #         id: ShardId(0),
72    /// #         total: 1,
73    /// #     };
74    /// #     let mut shard = Shard::new(mutex.clone(), "", shard_info, GatewayIntents::all(), None).await?;
75    /// #
76    /// use serenity::model::id::GuildId;
77    ///
78    /// shard.chunk_guild(GuildId::new(81384788765712384), Some(2000), false, ChunkGuildFilter::None, None);
79    /// # Ok(())
80    /// # }
81    /// ```
82    ///
83    /// Chunk a single guild by Id, limiting to 20 members, specifying a query parameter of `"do"`
84    /// and a nonce of `"request"`:
85    ///
86    /// ```rust,no_run
87    /// # use tokio::sync::Mutex;
88    /// # use serenity::model::gateway::{GatewayIntents, ShardInfo};
89    /// # use serenity::model::id::ShardId;
90    /// # use serenity::gateway::{ChunkGuildFilter, Shard};
91    /// # use std::sync::Arc;
92    /// #
93    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
94    /// #     let mutex = Arc::new(Mutex::new("".to_string()));
95    /// #
96    /// #     let shard_info = ShardInfo {
97    /// #         id: ShardId(0),
98    /// #         total: 1,
99    /// #     };
100    /// #
101    /// #     let mut shard = Shard::new(mutex.clone(), "", shard_info, GatewayIntents::all(), None).await?;;
102    /// #
103    /// use serenity::model::id::GuildId;
104    ///
105    /// shard.chunk_guild(
106    ///     GuildId::new(81384788765712384),
107    ///     Some(20),
108    ///     false,
109    ///     ChunkGuildFilter::Query("do".to_owned()),
110    ///     Some("request"),
111    /// );
112    /// # Ok(())
113    /// # }
114    /// ```
115    pub fn chunk_guild(
116        &self,
117        guild_id: GuildId,
118        limit: Option<u16>,
119        presences: bool,
120        filter: ChunkGuildFilter,
121        nonce: Option<String>,
122    ) {
123        self.send_to_shard(ShardRunnerMessage::ChunkGuild {
124            guild_id,
125            limit,
126            presences,
127            filter,
128            nonce,
129        });
130    }
131
132    /// Sets the user's current activity, if any.
133    ///
134    /// Other presence settings are maintained.
135    ///
136    /// # Examples
137    ///
138    /// Setting the current activity to playing `"Heroes of the Storm"`:
139    ///
140    /// ```rust,no_run
141    /// # use tokio::sync::Mutex;
142    /// # use serenity::gateway::{Shard};
143    /// # use serenity::model::id::ShardId;
144    /// # use serenity::model::gateway::{GatewayIntents, ShardInfo};
145    /// # use std::sync::Arc;
146    /// #
147    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
148    /// #     let mutex = Arc::new(Mutex::new("".to_string()));
149    /// #
150    /// #     let shard_info = ShardInfo {
151    /// #         id: ShardId(0),
152    /// #         total: 1,
153    /// #     };
154    /// #
155    /// #     let mut shard = Shard::new(mutex.clone(), "", shard_info, GatewayIntents::all(), None).await?;
156    /// use serenity::gateway::ActivityData;
157    ///
158    /// shard.set_activity(Some(ActivityData::playing("Heroes of the Storm")));
159    /// # Ok(())
160    /// # }
161    /// ```
162    pub fn set_activity(&self, activity: Option<ActivityData>) {
163        self.send_to_shard(ShardRunnerMessage::SetActivity(activity));
164    }
165
166    /// Sets the user's full presence information.
167    ///
168    /// Consider using the individual setters if you only need to modify one of these.
169    ///
170    /// # Examples
171    ///
172    /// Set the current user as playing `"Heroes of the Storm"` and being online:
173    ///
174    /// ```rust,ignore
175    /// # use tokio::sync::Mutex;
176    /// # use serenity::gateway::Shard;
177    /// # use std::sync::Arc;
178    /// #
179    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
180    /// #     let mutex = Arc::new(Mutex::new("".to_string()));
181    /// #
182    /// #     let shard_info = ShardInfo {
183    /// #         id: 0,
184    /// #         total: 1,
185    /// #     };
186    /// #
187    /// #     let mut shard = Shard::new(mutex.clone(), "", shard_info, None).await?;
188    /// #
189    /// use serenity::gateway::ActivityData;
190    /// use serenity::model::user::OnlineStatus;
191    ///
192    /// let activity = ActivityData::playing("Heroes of the Storm");
193    /// shard.set_presence(Some(activity), OnlineStatus::Online);
194    /// # Ok(())
195    /// # }
196    /// ```
197    pub fn set_presence(&self, activity: Option<ActivityData>, mut status: OnlineStatus) {
198        if status == OnlineStatus::Offline {
199            status = OnlineStatus::Invisible;
200        }
201
202        self.send_to_shard(ShardRunnerMessage::SetPresence(activity, status));
203    }
204
205    /// Sets the user's current online status.
206    ///
207    /// Note that [`Offline`] is not a valid online status, so it is automatically converted to
208    /// [`Invisible`].
209    ///
210    /// Other presence settings are maintained.
211    ///
212    /// # Examples
213    ///
214    /// Setting the current online status for the shard to [`DoNotDisturb`].
215    ///
216    /// ```rust,no_run
217    /// # use tokio::sync::Mutex;
218    /// # use serenity::gateway::{Shard};
219    /// # use serenity::model::id::ShardId;
220    /// # use serenity::model::gateway::{GatewayIntents, ShardInfo};
221    /// # use std::sync::Arc;
222    /// #
223    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
224    /// #     let mutex = Arc::new(Mutex::new("".to_string()));
225    /// #     let shard_info = ShardInfo {
226    /// #         id: ShardId(0),
227    /// #         total: 1,
228    /// #     };
229    /// #
230    /// #     let mut shard = Shard::new(mutex.clone(), "", shard_info, GatewayIntents::all(), None).await?;
231    /// #
232    /// use serenity::model::user::OnlineStatus;
233    ///
234    /// shard.set_status(OnlineStatus::DoNotDisturb);
235    /// # Ok(())
236    /// # }
237    /// ```
238    ///
239    /// [`DoNotDisturb`]: OnlineStatus::DoNotDisturb
240    /// [`Invisible`]: OnlineStatus::Invisible
241    /// [`Offline`]: OnlineStatus::Offline
242    pub fn set_status(&self, mut online_status: OnlineStatus) {
243        if online_status == OnlineStatus::Offline {
244            online_status = OnlineStatus::Invisible;
245        }
246
247        self.send_to_shard(ShardRunnerMessage::SetStatus(online_status));
248    }
249
250    /// Shuts down the websocket by attempting to cleanly close the connection.
251    pub fn shutdown_clean(&self) {
252        self.send_to_shard(ShardRunnerMessage::Close(1000, None));
253    }
254
255    /// Sends a raw message over the WebSocket.
256    ///
257    /// The given message is not mutated in any way, and is sent as-is.
258    ///
259    /// You should only use this if you know what you're doing. If you're wanting to, for example,
260    /// send a presence update, prefer the usage of the [`Self::set_presence`] method.
261    pub fn websocket_message(&self, message: Message) {
262        self.send_to_shard(ShardRunnerMessage::Message(message));
263    }
264
265    /// Sends a message to the shard.
266    #[inline]
267    pub fn send_to_shard(&self, msg: ShardRunnerMessage) {
268        if let Err(e) = self.tx.unbounded_send(msg) {
269            tracing::warn!("failed to send ShardRunnerMessage to shard: {}", e);
270        }
271    }
272
273    #[cfg(feature = "collector")]
274    pub fn add_collector(&self, collector: CollectorCallback) {
275        self.collectors.lock().expect("poison").push(collector);
276    }
277}
278
279impl AsRef<ShardMessenger> for ShardMessenger {
280    fn as_ref(&self) -> &ShardMessenger {
281        self
282    }
283}