serenity/client/mod.rs
1//! A module for [`Client`] and supporting types.
2//!
3//! The Client contains information about a single bot's token, as well as event handlers.
4//! Dispatching events to configured handlers and starting the shards' connections are handled
5//! directly via the client. In addition, the `http` module and `Cache` are also automatically
6//! handled by the Client module for you.
7//!
8//! A [`Context`] is provided for every handler.
9//!
10//! The `http` module is the lower-level method of interacting with the Discord REST API.
11//! Realistically, there should be little reason to use this yourself, as the Context will do this
12//! for you. A possible use case of using the `http` module is if you do not have a Cache, for
13//! purposes such as low memory requirements.
14//!
15//! Click [here][Client examples] for an example on how to use a `Client`.
16//!
17//! [Client examples]: Client#examples
18
19mod context;
20#[cfg(feature = "gateway")]
21pub(crate) mod dispatch;
22mod error;
23#[cfg(feature = "gateway")]
24mod event_handler;
25
26use std::future::IntoFuture;
27use std::ops::Range;
28use std::sync::Arc;
29#[cfg(feature = "framework")]
30use std::sync::OnceLock;
31
32use futures::channel::mpsc::UnboundedReceiver as Receiver;
33use futures::future::BoxFuture;
34use futures::StreamExt as _;
35use tokio::sync::{Mutex, RwLock};
36use tracing::{debug, error, info, instrument};
37use typemap_rev::{TypeMap, TypeMapKey};
38
39pub use self::context::Context;
40pub use self::error::Error as ClientError;
41#[cfg(feature = "gateway")]
42pub use self::event_handler::{EventHandler, FullEvent, RawEventHandler};
43#[cfg(feature = "gateway")]
44use super::gateway::GatewayError;
45#[cfg(feature = "cache")]
46pub use crate::cache::Cache;
47#[cfg(feature = "cache")]
48use crate::cache::Settings as CacheSettings;
49#[cfg(feature = "framework")]
50use crate::framework::Framework;
51#[cfg(feature = "voice")]
52use crate::gateway::VoiceGatewayManager;
53use crate::gateway::{ActivityData, PresenceData};
54#[cfg(feature = "gateway")]
55use crate::gateway::{ShardManager, ShardManagerOptions};
56use crate::http::Http;
57use crate::internal::prelude::*;
58#[cfg(feature = "gateway")]
59use crate::model::gateway::GatewayIntents;
60use crate::model::id::ApplicationId;
61use crate::model::user::OnlineStatus;
62
63/// A builder implementing [`IntoFuture`] building a [`Client`] to interact with Discord.
64#[cfg(feature = "gateway")]
65#[must_use = "Builders do nothing unless they are awaited"]
66pub struct ClientBuilder {
67 data: TypeMap,
68 http: Http,
69 intents: GatewayIntents,
70 #[cfg(feature = "cache")]
71 cache_settings: CacheSettings,
72 #[cfg(feature = "framework")]
73 framework: Option<Box<dyn Framework>>,
74 #[cfg(feature = "voice")]
75 voice_manager: Option<Arc<dyn VoiceGatewayManager>>,
76 event_handlers: Vec<Arc<dyn EventHandler>>,
77 raw_event_handlers: Vec<Arc<dyn RawEventHandler>>,
78 presence: PresenceData,
79}
80
81#[cfg(feature = "gateway")]
82impl ClientBuilder {
83 fn new_(http: Http, intents: GatewayIntents) -> Self {
84 Self {
85 data: TypeMap::new(),
86 http,
87 intents,
88 #[cfg(feature = "cache")]
89 cache_settings: CacheSettings::default(),
90 #[cfg(feature = "framework")]
91 framework: None,
92 #[cfg(feature = "voice")]
93 voice_manager: None,
94 event_handlers: vec![],
95 raw_event_handlers: vec![],
96 presence: PresenceData::default(),
97 }
98 }
99
100 /// Construct a new builder to call methods on for the client construction. The `token` will
101 /// automatically be prefixed "Bot " if not already.
102 ///
103 /// **Panic**: If you have enabled the `framework`-feature (on by default), you must specify a
104 /// framework via the [`Self::framework`] method, otherwise awaiting the builder will cause a
105 /// panic.
106 pub fn new(token: impl AsRef<str>, intents: GatewayIntents) -> Self {
107 Self::new_(Http::new(token.as_ref()), intents)
108 }
109
110 /// Construct a new builder with a [`Http`] instance to calls methods on for the client
111 /// construction.
112 ///
113 /// **Panic**: If you have enabled the `framework`-feature (on by default), you must specify a
114 /// framework via the [`Self::framework`] method, otherwise awaiting the builder will cause a
115 /// panic.
116 pub fn new_with_http(http: Http, intents: GatewayIntents) -> Self {
117 Self::new_(http, intents)
118 }
119
120 /// Sets a token for the bot. If the token is not prefixed "Bot ", this method will
121 /// automatically do so.
122 pub fn token(mut self, token: impl AsRef<str>) -> Self {
123 self.http = Http::new(token.as_ref());
124
125 self
126 }
127
128 /// Gets the current token used for the [`Http`] client.
129 pub fn get_token(&self) -> &str {
130 self.http.token()
131 }
132
133 /// Sets the application id.
134 pub fn application_id(self, application_id: ApplicationId) -> Self {
135 self.http.set_application_id(application_id);
136
137 self
138 }
139
140 /// Gets the application ID, if already initialized. See [`Self::application_id`] for more
141 /// info.
142 pub fn get_application_id(&self) -> Option<ApplicationId> {
143 self.http.application_id()
144 }
145
146 /// Sets the entire [`TypeMap`] that will be available in [`Context`]s. A [`TypeMap`] must not
147 /// be constructed manually: [`Self::type_map_insert`] can be used to insert one type at a
148 /// time.
149 pub fn type_map(mut self, type_map: TypeMap) -> Self {
150 self.data = type_map;
151
152 self
153 }
154
155 /// Gets the type map. See [`Self::type_map`] for more info.
156 pub fn get_type_map(&self) -> &TypeMap {
157 &self.data
158 }
159
160 /// Insert a single `value` into the internal [`TypeMap`] that will be available in
161 /// [`Context::data`]. This method can be called multiple times in order to populate the
162 /// [`TypeMap`] with `value`s.
163 pub fn type_map_insert<T: TypeMapKey>(mut self, value: T::Value) -> Self {
164 self.data.insert::<T>(value);
165
166 self
167 }
168
169 /// Sets the settings of the cache. Refer to [`Settings`] for more information.
170 ///
171 /// [`Settings`]: CacheSettings
172 #[cfg(feature = "cache")]
173 pub fn cache_settings(mut self, settings: CacheSettings) -> Self {
174 self.cache_settings = settings;
175 self
176 }
177
178 /// Gets the cache settings. See [`Self::cache_settings`] for more info.
179 #[cfg(feature = "cache")]
180 pub fn get_cache_settings(&self) -> &CacheSettings {
181 &self.cache_settings
182 }
183
184 /// Sets the command framework to be used. It will receive messages sent over the gateway and
185 /// then consider - based on its settings - whether to dispatch a command.
186 ///
187 /// *Info*: If a reference to the framework is required for manual dispatch, you can implement
188 /// [`Framework`] on [`Arc<YourFrameworkType>`] instead of `YourFrameworkType`.
189 #[cfg(feature = "framework")]
190 pub fn framework<F>(mut self, framework: F) -> Self
191 where
192 F: Framework + 'static,
193 {
194 self.framework = Some(Box::new(framework));
195
196 self
197 }
198
199 /// Gets the framework, if already initialized. See [`Self::framework`] for more info.
200 #[cfg(feature = "framework")]
201 pub fn get_framework(&self) -> Option<&dyn Framework> {
202 self.framework.as_deref()
203 }
204
205 /// Sets the voice gateway handler to be used. It will receive voice events sent over the
206 /// gateway and then consider - based on its settings - whether to dispatch a command.
207 ///
208 /// *Info*: If a reference to the voice_manager is required for manual dispatch, use the
209 /// [`Self::voice_manager_arc`]-method instead.
210 #[cfg(feature = "voice")]
211 pub fn voice_manager<V>(mut self, voice_manager: V) -> Self
212 where
213 V: VoiceGatewayManager + 'static,
214 {
215 self.voice_manager = Some(Arc::new(voice_manager));
216
217 self
218 }
219
220 /// This method allows to pass an [`Arc`]'ed `voice_manager` - this step is done for you in the
221 /// [`voice_manager`]-method, if you don't need the extra control. You can provide a clone and
222 /// keep the original to manually dispatch.
223 ///
224 /// [`voice_manager`]: Self::voice_manager
225 #[cfg(feature = "voice")]
226 pub fn voice_manager_arc(
227 mut self,
228 voice_manager: Arc<dyn VoiceGatewayManager + 'static>,
229 ) -> Self {
230 self.voice_manager = Some(voice_manager);
231
232 self
233 }
234
235 /// Gets the voice manager, if already initialized. See [`Self::voice_manager`] for more info.
236 #[cfg(feature = "voice")]
237 pub fn get_voice_manager(&self) -> Option<Arc<dyn VoiceGatewayManager>> {
238 self.voice_manager.clone()
239 }
240
241 /// Sets all intents directly, replacing already set intents. Intents are a bitflag, you can
242 /// combine them by performing the `|`-operator.
243 ///
244 /// # What are Intents
245 ///
246 /// A [gateway intent] sets the types of gateway events (e.g. member joins, guild integrations,
247 /// guild emoji updates, ...) the bot shall receive. Carefully picking the needed intents
248 /// greatly helps the bot to scale, as less intents will result in less events to be received
249 /// hence less processed by the bot.
250 ///
251 /// # Privileged Intents
252 ///
253 /// The intents [`GatewayIntents::GUILD_PRESENCES`], [`GatewayIntents::GUILD_MEMBERS`] and
254 /// [`GatewayIntents::MESSAGE_CONTENT`] are *privileged*. [Privileged intents] need to be
255 /// enabled in the *developer portal*. Once the bot is in 100 guilds or more, [the bot must be
256 /// verified] in order to use privileged intents.
257 ///
258 /// [gateway intent]: https://discord.com/developers/docs/topics/gateway#privileged-intents
259 /// [Privileged intents]: https://discord.com/developers/docs/topics/gateway#privileged-intents
260 /// [the bot must be verified]: https://support.discord.com/hc/en-us/articles/360040720412-Bot-Verification-and-Data-Whitelisting
261 pub fn intents(mut self, intents: GatewayIntents) -> Self {
262 self.intents = intents;
263
264 self
265 }
266
267 /// Gets the intents. See [`Self::intents`] for more info.
268 pub fn get_intents(&self) -> GatewayIntents {
269 self.intents
270 }
271
272 /// Adds an event handler with multiple methods for each possible event.
273 pub fn event_handler<H: EventHandler + 'static>(mut self, event_handler: H) -> Self {
274 self.event_handlers.push(Arc::new(event_handler));
275
276 self
277 }
278
279 /// Adds an event handler with multiple methods for each possible event. Passed by Arc.
280 pub fn event_handler_arc<H: EventHandler + 'static>(
281 mut self,
282 event_handler_arc: Arc<H>,
283 ) -> Self {
284 self.event_handlers.push(event_handler_arc);
285
286 self
287 }
288
289 /// Gets the added event handlers. See [`Self::event_handler`] for more info.
290 pub fn get_event_handlers(&self) -> &[Arc<dyn EventHandler>] {
291 &self.event_handlers
292 }
293
294 /// Adds an event handler with a single method where all received gateway events will be
295 /// dispatched.
296 pub fn raw_event_handler<H: RawEventHandler + 'static>(mut self, raw_event_handler: H) -> Self {
297 self.raw_event_handlers.push(Arc::new(raw_event_handler));
298
299 self
300 }
301
302 /// Gets the added raw event handlers. See [`Self::raw_event_handler`] for more info.
303 pub fn get_raw_event_handlers(&self) -> &[Arc<dyn RawEventHandler>] {
304 &self.raw_event_handlers
305 }
306
307 /// Sets the initial activity.
308 pub fn activity(mut self, activity: ActivityData) -> Self {
309 self.presence.activity = Some(activity);
310
311 self
312 }
313
314 /// Sets the initial status.
315 pub fn status(mut self, status: OnlineStatus) -> Self {
316 self.presence.status = status;
317
318 self
319 }
320
321 /// Gets the initial presence. See [`Self::activity`] and [`Self::status`] for more info.
322 pub fn get_presence(&self) -> &PresenceData {
323 &self.presence
324 }
325}
326
327#[cfg(feature = "gateway")]
328impl IntoFuture for ClientBuilder {
329 type Output = Result<Client>;
330
331 type IntoFuture = BoxFuture<'static, Result<Client>>;
332
333 #[instrument(skip(self))]
334 fn into_future(self) -> Self::IntoFuture {
335 let data = Arc::new(RwLock::new(self.data));
336 #[cfg(feature = "framework")]
337 let framework = self.framework;
338 let event_handlers = self.event_handlers;
339 let raw_event_handlers = self.raw_event_handlers;
340 let intents = self.intents;
341 let presence = self.presence;
342
343 let mut http = self.http;
344
345 if let Some(ratelimiter) = &mut http.ratelimiter {
346 let event_handlers_clone = event_handlers.clone();
347 ratelimiter.set_ratelimit_callback(Box::new(move |info| {
348 for event_handler in event_handlers_clone.iter().map(Arc::clone) {
349 let info = info.clone();
350 tokio::spawn(async move { event_handler.ratelimit(info).await });
351 }
352 }));
353 }
354
355 let http = Arc::new(http);
356
357 #[cfg(feature = "voice")]
358 let voice_manager = self.voice_manager;
359
360 #[cfg(feature = "cache")]
361 let cache = Arc::new(Cache::new_with_settings(self.cache_settings));
362
363 Box::pin(async move {
364 let ws_url = Arc::new(Mutex::new(match http.get_gateway().await {
365 Ok(response) => response.url,
366 Err(err) => {
367 tracing::warn!("HTTP request to get gateway URL failed: {}", err);
368 "wss://gateway.discord.gg".to_string()
369 },
370 }));
371
372 #[cfg(feature = "framework")]
373 let framework_cell = Arc::new(OnceLock::new());
374 let (shard_manager, shard_manager_ret_value) = ShardManager::new(ShardManagerOptions {
375 data: Arc::clone(&data),
376 event_handlers,
377 raw_event_handlers,
378 #[cfg(feature = "framework")]
379 framework: Arc::clone(&framework_cell),
380 shard_index: 0,
381 shard_init: 0,
382 shard_total: 0,
383 #[cfg(feature = "voice")]
384 voice_manager: voice_manager.clone(),
385 ws_url: Arc::clone(&ws_url),
386 #[cfg(feature = "cache")]
387 cache: Arc::clone(&cache),
388 http: Arc::clone(&http),
389 intents,
390 presence: Some(presence),
391 });
392
393 let client = Client {
394 data,
395 shard_manager,
396 shard_manager_return_value: shard_manager_ret_value,
397 #[cfg(feature = "voice")]
398 voice_manager,
399 ws_url,
400 #[cfg(feature = "cache")]
401 cache,
402 http,
403 };
404 #[cfg(feature = "framework")]
405 if let Some(mut framework) = framework {
406 framework.init(&client).await;
407 if let Err(_existing) = framework_cell.set(framework.into()) {
408 tracing::warn!("overwrote existing contents of framework OnceLock");
409 }
410 }
411 Ok(client)
412 })
413 }
414}
415
416/// A wrapper for HTTP and gateway connections.
417///
418/// The Client is the way to be able to start sending authenticated requests over the REST API, as
419/// well as initializing a WebSocket connection through [`Shard`]s. Refer to the [documentation on
420/// using sharding][sharding docs] for more information.
421///
422/// # Event Handlers
423///
424/// Event handlers can be configured. For example, the event handler [`EventHandler::message`] will
425/// be dispatched to whenever a [`Event::MessageCreate`] is received over the connection.
426///
427/// Note that you do not need to manually handle events, as they are handled internally and then
428/// dispatched to your event handlers.
429///
430/// # Examples
431///
432/// Creating a Client instance and adding a handler on every message receive, acting as a
433/// "ping-pong" bot is simple:
434///
435/// ```no_run
436/// use serenity::model::prelude::*;
437/// use serenity::prelude::*;
438/// use serenity::Client;
439///
440/// struct Handler;
441///
442/// #[serenity::async_trait]
443/// impl EventHandler for Handler {
444/// async fn message(&self, context: Context, msg: Message) {
445/// if msg.content == "!ping" {
446/// let _ = msg.channel_id.say(&context, "Pong!");
447/// }
448/// }
449/// }
450///
451/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
452/// let mut client =
453/// Client::builder("my token here", GatewayIntents::default()).event_handler(Handler).await?;
454///
455/// client.start().await?;
456/// # Ok(())
457/// # }
458/// ```
459///
460/// [`Shard`]: crate::gateway::Shard
461/// [`Event::MessageCreate`]: crate::model::event::Event::MessageCreate
462/// [sharding docs]: crate::gateway#sharding
463#[cfg(feature = "gateway")]
464pub struct Client {
465 /// A TypeMap which requires types to be Send + Sync. This is a map that can be safely shared
466 /// across contexts.
467 ///
468 /// The purpose of the data field is to be accessible and persistent across contexts; that is,
469 /// data can be modified by one context, and will persist through the future and be accessible
470 /// through other contexts. This is useful for anything that should "live" through the program:
471 /// counters, database connections, custom user caches, etc.
472 ///
473 /// In the meaning of a context, this data can be accessed through [`Context::data`].
474 ///
475 /// # Examples
476 ///
477 /// Create a `MessageEventCounter` to track the following events:
478 ///
479 /// - [`Event::MessageCreate`]
480 /// - [`Event::MessageDelete`]
481 /// - [`Event::MessageDeleteBulk`]
482 /// - [`Event::MessageUpdate`]
483 ///
484 /// ```rust,ignore
485 /// use std::collections::HashMap;
486 /// use std::env;
487 ///
488 /// use serenity::model::prelude::*;
489 /// use serenity::prelude::*;
490 ///
491 /// struct MessageEventCounter;
492 ///
493 /// impl TypeMapKey for MessageEventCounter {
494 /// type Value = HashMap<String, u64>;
495 /// }
496 ///
497 /// async fn reg<S: Into<String>>(ctx: Context, name: S) {
498 /// let mut data = ctx.data.write().await;
499 /// let counter = data.get_mut::<MessageEventCounter>().unwrap();
500 /// let entry = counter.entry(name.into()).or_insert(0);
501 /// *entry += 1;
502 /// }
503 ///
504 /// struct Handler;
505 ///
506 /// #[serenity::async_trait]
507 /// impl EventHandler for Handler {
508 /// async fn message(&self, ctx: Context, _: Message) {
509 /// reg(ctx, "MessageCreate").await
510 /// }
511 /// async fn message_delete(&self, ctx: Context, _: ChannelId, _: MessageId) {
512 /// reg(ctx, "MessageDelete").await
513 /// }
514 /// async fn message_delete_bulk(&self, ctx: Context, _: ChannelId, _: Vec<MessageId>) {
515 /// reg(ctx, "MessageDeleteBulk").await
516 /// }
517 ///
518 /// #[cfg(feature = "cache")]
519 /// async fn message_update(
520 /// &self,
521 /// ctx: Context,
522 /// _old: Option<Message>,
523 /// _new: Option<Message>,
524 /// _: MessageUpdateEvent,
525 /// ) {
526 /// reg(ctx, "MessageUpdate").await
527 /// }
528 ///
529 /// #[cfg(not(feature = "cache"))]
530 /// async fn message_update(&self, ctx: Context, _new_data: MessageUpdateEvent) {
531 /// reg(ctx, "MessageUpdate").await
532 /// }
533 /// }
534 ///
535 /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
536 /// let token = std::env::var("DISCORD_TOKEN")?;
537 /// let mut client = Client::builder(&token, GatewayIntents::default()).event_handler(Handler).await?;
538 /// {
539 /// let mut data = client.data.write().await;
540 /// data.insert::<MessageEventCounter>(HashMap::default());
541 /// }
542 ///
543 /// client.start().await?;
544 /// # Ok(())
545 /// # }
546 /// ```
547 ///
548 /// Refer to [example 05] for an example on using the [`Self::data`] field.
549 ///
550 /// [`Event::MessageCreate`]: crate::model::event::Event::MessageCreate
551 /// [`Event::MessageDelete`]: crate::model::event::Event::MessageDelete
552 /// [`Event::MessageDeleteBulk`]: crate::model::event::Event::MessageDeleteBulk
553 /// [`Event::MessageUpdate`]: crate::model::event::Event::MessageUpdate
554 /// [example 05]: https://github.com/serenity-rs/serenity/tree/current/examples/e05_command_framework
555 pub data: Arc<RwLock<TypeMap>>,
556 /// A HashMap of all shards instantiated by the Client.
557 ///
558 /// The key is the shard ID and the value is the shard itself.
559 ///
560 /// # Examples
561 ///
562 /// If you call [`client.start_shard(3, 5)`][`Client::start_shard`], this HashMap will only
563 /// ever contain a single key of `3`, as that's the only Shard the client is responsible for.
564 ///
565 /// If you call [`client.start_shards(10)`][`Client::start_shards`], this HashMap will contain
566 /// keys 0 through 9, one for each shard handled by the client.
567 ///
568 /// Printing the number of shards currently instantiated by the client every 5 seconds:
569 ///
570 /// ```rust,no_run
571 /// # use serenity::prelude::*;
572 /// # use std::time::Duration;
573 /// #
574 /// # fn run(client: Client) {
575 /// // Create a clone of the `Arc` containing the shard manager.
576 /// let shard_manager = client.shard_manager.clone();
577 ///
578 /// tokio::spawn(async move {
579 /// loop {
580 /// let count = shard_manager.shards_instantiated().await.len();
581 /// println!("Shard count instantiated: {}", count);
582 ///
583 /// tokio::time::sleep(Duration::from_millis(5000)).await;
584 /// }
585 /// });
586 /// # }
587 /// ```
588 ///
589 /// Shutting down all connections after one minute of operation:
590 ///
591 /// ```rust,no_run
592 /// # use serenity::prelude::*;
593 /// # use std::time::Duration;
594 /// #
595 /// # fn run(client: Client) {
596 /// // Create a clone of the `Arc` containing the shard manager.
597 /// let shard_manager = client.shard_manager.clone();
598 ///
599 /// // Create a thread which will sleep for 60 seconds and then have the shard manager
600 /// // shutdown.
601 /// tokio::spawn(async move {
602 /// tokio::time::sleep(Duration::from_secs(60)).await;
603 ///
604 /// shard_manager.shutdown_all().await;
605 ///
606 /// println!("Shutdown shard manager!");
607 /// });
608 /// # }
609 /// ```
610 pub shard_manager: Arc<ShardManager>,
611 shard_manager_return_value: Receiver<Result<(), GatewayError>>,
612 /// The voice manager for the client.
613 ///
614 /// This is an ergonomic structure for interfacing over shards' voice
615 /// connections.
616 #[cfg(feature = "voice")]
617 pub voice_manager: Option<Arc<dyn VoiceGatewayManager + 'static>>,
618 /// URL that the client's shards will use to connect to the gateway.
619 ///
620 /// This is likely not important for production usage and is, at best, used for debugging.
621 ///
622 /// This is wrapped in an `Arc<Mutex<T>>` so all shards will have an updated value available.
623 pub ws_url: Arc<Mutex<String>>,
624 /// The cache for the client.
625 #[cfg(feature = "cache")]
626 pub cache: Arc<Cache>,
627 /// An HTTP client.
628 pub http: Arc<Http>,
629}
630
631impl Client {
632 pub fn builder(token: impl AsRef<str>, intents: GatewayIntents) -> ClientBuilder {
633 ClientBuilder::new(token, intents)
634 }
635
636 /// Establish the connection and start listening for events.
637 ///
638 /// This will start receiving events in a loop and start dispatching the events to your
639 /// registered handlers.
640 ///
641 /// Note that this should be used only for users and for bots which are in less than 2500
642 /// guilds. If you have a reason for sharding and/or are in more than 2500 guilds, use one of
643 /// these depending on your use case:
644 ///
645 /// Refer to the [Gateway documentation][gateway docs] for more information on effectively
646 /// using sharding.
647 ///
648 /// # Examples
649 ///
650 /// Starting a Client with only 1 shard, out of 1 total:
651 ///
652 /// ```rust,no_run
653 /// # use std::error::Error;
654 /// # use serenity::prelude::*;
655 /// use serenity::Client;
656 ///
657 /// # async fn run() -> Result<(), Box<dyn Error>> {
658 /// let token = std::env::var("DISCORD_TOKEN")?;
659 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
660 ///
661 /// if let Err(why) = client.start().await {
662 /// println!("Err with client: {:?}", why);
663 /// }
664 /// # Ok(())
665 /// # }
666 /// ```
667 ///
668 /// [gateway docs]: crate::gateway#sharding
669 #[instrument(skip(self))]
670 pub async fn start(&mut self) -> Result<()> {
671 self.start_connection(0, 0, 1).await
672 }
673
674 /// Establish the connection(s) and start listening for events.
675 ///
676 /// This will start receiving events in a loop and start dispatching the events to your
677 /// registered handlers.
678 ///
679 /// This will retrieve an automatically determined number of shards to use from the API -
680 /// determined by Discord - and then open a number of shards equivalent to that amount.
681 ///
682 /// Refer to the [Gateway documentation][gateway docs] for more information on effectively
683 /// using sharding.
684 ///
685 /// # Examples
686 ///
687 /// Start as many shards as needed using autosharding:
688 ///
689 /// ```rust,no_run
690 /// # use std::error::Error;
691 /// # use serenity::prelude::*;
692 /// use serenity::Client;
693 ///
694 /// # async fn run() -> Result<(), Box<dyn Error>> {
695 /// let token = std::env::var("DISCORD_TOKEN")?;
696 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
697 ///
698 /// if let Err(why) = client.start_autosharded().await {
699 /// println!("Err with client: {:?}", why);
700 /// }
701 /// # Ok(())
702 /// # }
703 /// ```
704 ///
705 /// # Errors
706 ///
707 /// Returns a [`ClientError::Shutdown`] when all shards have shutdown due to an error.
708 ///
709 /// [gateway docs]: crate::gateway#sharding
710 #[instrument(skip(self))]
711 pub async fn start_autosharded(&mut self) -> Result<()> {
712 let (end, total) = {
713 let res = self.http.get_bot_gateway().await?;
714
715 (res.shards - 1, res.shards)
716 };
717
718 self.start_connection(0, end, total).await
719 }
720
721 /// Establish a sharded connection and start listening for events.
722 ///
723 /// This will start receiving events and dispatch them to your registered handlers.
724 ///
725 /// This will create a single shard by ID. If using one shard per process, you will need to
726 /// start other processes with the other shard IDs in some way.
727 ///
728 /// Refer to the [Gateway documentation][gateway docs] for more information on effectively
729 /// using sharding.
730 ///
731 /// # Examples
732 ///
733 /// Start shard 3 of 5:
734 ///
735 /// ```rust,no_run
736 /// # use std::error::Error;
737 /// # use serenity::prelude::*;
738 /// use serenity::Client;
739 ///
740 /// # async fn run() -> Result<(), Box<dyn Error>> {
741 /// let token = std::env::var("DISCORD_TOKEN")?;
742 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
743 ///
744 /// if let Err(why) = client.start_shard(3, 5).await {
745 /// println!("Err with client: {:?}", why);
746 /// }
747 /// # Ok(())
748 /// # }
749 /// ```
750 ///
751 /// Start shard 0 of 1 (you may also be interested in [`Self::start`] or
752 /// [`Self::start_autosharded`]):
753 ///
754 /// ```rust,no_run
755 /// # use std::error::Error;
756 /// # use serenity::prelude::*;
757 /// use serenity::Client;
758 ///
759 /// # async fn run() -> Result<(), Box<dyn Error>> {
760 /// let token = std::env::var("DISCORD_TOKEN")?;
761 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
762 ///
763 /// if let Err(why) = client.start_shard(0, 1).await {
764 /// println!("Err with client: {:?}", why);
765 /// }
766 /// # Ok(())
767 /// # }
768 /// ```
769 ///
770 /// # Errors
771 ///
772 /// Returns a [`ClientError::Shutdown`] when all shards have shutdown due to an error.
773 ///
774 /// [gateway docs]: crate::gateway#sharding
775 #[instrument(skip(self))]
776 pub async fn start_shard(&mut self, shard: u32, shards: u32) -> Result<()> {
777 self.start_connection(shard, shard, shards).await
778 }
779
780 /// Establish sharded connections and start listening for events.
781 ///
782 /// This will start receiving events and dispatch them to your registered handlers.
783 ///
784 /// This will create and handle all shards within this single process. If you only need to
785 /// start a single shard within the process, or a range of shards, use [`Self::start_shard`] or
786 /// [`Self::start_shard_range`], respectively.
787 ///
788 /// Refer to the [Gateway documentation][gateway docs] for more information on effectively
789 /// using sharding.
790 ///
791 /// # Examples
792 ///
793 /// Start all of 8 shards:
794 ///
795 /// ```rust,no_run
796 /// # use std::error::Error;
797 /// # use serenity::prelude::*;
798 /// use serenity::Client;
799 ///
800 /// # async fn run() -> Result<(), Box<dyn Error>> {
801 /// let token = std::env::var("DISCORD_TOKEN")?;
802 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
803 ///
804 /// if let Err(why) = client.start_shards(8).await {
805 /// println!("Err with client: {:?}", why);
806 /// }
807 /// # Ok(())
808 /// # }
809 /// ```
810 ///
811 /// # Errors
812 ///
813 /// Returns a [`ClientError::Shutdown`] when all shards have shutdown due to an error.
814 ///
815 /// [Gateway docs]: crate::gateway#sharding
816 #[instrument(skip(self))]
817 pub async fn start_shards(&mut self, total_shards: u32) -> Result<()> {
818 self.start_connection(0, total_shards - 1, total_shards).await
819 }
820
821 /// Establish a range of sharded connections and start listening for events.
822 ///
823 /// This will start receiving events and dispatch them to your registered handlers.
824 ///
825 /// This will create and handle all shards within a given range within this single process. If
826 /// you only need to start a single shard within the process, or all shards within the process,
827 /// use [`Self::start_shard`] or [`Self::start_shards`], respectively.
828 ///
829 /// Refer to the [Gateway documentation][gateway docs] for more information on effectively
830 /// using sharding.
831 ///
832 /// # Examples
833 ///
834 /// For a bot using a total of 10 shards, initialize shards 4 through 7:
835 ///
836 /// ```rust,no_run
837 /// # use std::error::Error;
838 /// # use serenity::prelude::*;
839 /// use serenity::Client;
840 ///
841 /// # async fn run() -> Result<(), Box<dyn Error>> {
842 /// let token = std::env::var("DISCORD_TOKEN")?;
843 /// let mut client = Client::builder(&token, GatewayIntents::default()).await?;
844 ///
845 /// if let Err(why) = client.start_shard_range(4..7, 10).await {
846 /// println!("Err with client: {:?}", why);
847 /// }
848 /// # Ok(())
849 /// # }
850 /// ```
851 ///
852 /// # Errors
853 ///
854 /// Returns a [`ClientError::Shutdown`] when all shards have shutdown due to an error.
855 ///
856 /// [Gateway docs]: crate::gateway#sharding
857 #[instrument(skip(self))]
858 pub async fn start_shard_range(&mut self, range: Range<u32>, total_shards: u32) -> Result<()> {
859 self.start_connection(range.start, range.end, total_shards).await
860 }
861
862 /// Shard data layout is:
863 /// 0: first shard number to initialize
864 /// 1: shard number to initialize up to and including
865 /// 2: total number of shards the bot is sharding for
866 ///
867 /// Not all shards need to be initialized in this process.
868 ///
869 /// # Errors
870 ///
871 /// Returns a [`ClientError::Shutdown`] when all shards have shutdown due to an error.
872 #[instrument(skip(self))]
873 async fn start_connection(
874 &mut self,
875 start_shard: u32,
876 end_shard: u32,
877 total_shards: u32,
878 ) -> Result<()> {
879 #[cfg(feature = "voice")]
880 if let Some(voice_manager) = &self.voice_manager {
881 let user = self.http.get_current_user().await?;
882
883 voice_manager.initialise(total_shards, user.id).await;
884 }
885
886 let init = end_shard - start_shard + 1;
887
888 self.shard_manager.set_shards(start_shard, init, total_shards).await;
889
890 debug!("Initializing shard info: {} - {}/{}", start_shard, init, total_shards);
891
892 if let Err(why) = self.shard_manager.initialize() {
893 error!("Failed to boot a shard: {:?}", why);
894 info!("Shutting down all shards");
895
896 self.shard_manager.shutdown_all().await;
897
898 return Err(Error::Client(ClientError::ShardBootFailure));
899 }
900
901 if let Some(Err(err)) = self.shard_manager_return_value.next().await {
902 return Err(Error::Gateway(err));
903 }
904
905 Ok(())
906 }
907}