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}