serenity/client/
context.rs

1use std::fmt;
2use std::sync::Arc;
3
4use tokio::sync::RwLock;
5use typemap_rev::TypeMap;
6
7#[cfg(feature = "cache")]
8pub use crate::cache::Cache;
9use crate::gateway::ActivityData;
10#[cfg(feature = "gateway")]
11use crate::gateway::{ShardMessenger, ShardRunner};
12use crate::http::Http;
13use crate::model::prelude::*;
14
15/// The context is a general utility struct provided on event dispatches.
16///
17/// The Context helps with dealing with the current "context" of the event dispatch. The context
18/// also acts as a general high-level interface over the associated [`Shard`] which received
19/// the event, or the low-level [`http`] module.
20///
21/// The context contains "shortcuts", like for interacting with the shard. Methods like
22/// [`Self::set_activity`] will unlock the shard and perform an update for you to save a bit of
23/// work.
24///
25/// A context will only live for the event it was dispatched for. After the event handler finished,
26/// it is destroyed and will not be re-used.
27///
28/// [`Shard`]: crate::gateway::Shard
29/// [`http`]: crate::http
30#[derive(Clone)]
31pub struct Context {
32    /// A clone of [`Client::data`]. Refer to its documentation for more information.
33    ///
34    /// [`Client::data`]: super::Client::data
35    pub data: Arc<RwLock<TypeMap>>,
36    /// The messenger to communicate with the shard runner.
37    pub shard: ShardMessenger,
38    /// The ID of the shard this context is related to.
39    pub shard_id: ShardId,
40    pub http: Arc<Http>,
41    #[cfg(feature = "cache")]
42    pub cache: Arc<Cache>,
43}
44
45// Used by the #[instrument] macro on client::dispatch::handle_event
46impl fmt::Debug for Context {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("Context")
49            .field("shard", &self.shard)
50            .field("shard_id", &self.shard_id)
51            .finish_non_exhaustive()
52    }
53}
54
55impl Context {
56    /// Create a new Context to be passed to an event handler.
57    #[cfg(feature = "gateway")]
58    pub(crate) fn new(
59        data: Arc<RwLock<TypeMap>>,
60        runner: &ShardRunner,
61        shard_id: ShardId,
62        http: Arc<Http>,
63        #[cfg(feature = "cache")] cache: Arc<Cache>,
64    ) -> Context {
65        Context {
66            shard: ShardMessenger::new(runner),
67            shard_id,
68            data,
69            http,
70            #[cfg(feature = "cache")]
71            cache,
72        }
73    }
74
75    #[cfg(all(not(feature = "cache"), not(feature = "gateway")))]
76    pub fn easy(data: Arc<RwLock<TypeMap>>, shard_id: u32, http: Arc<Http>) -> Context {
77        Context {
78            shard_id,
79            data,
80            http,
81        }
82    }
83
84    /// Sets the current user as being [`Online`]. This maintains the current activity.
85    ///
86    /// # Examples
87    ///
88    /// Set the current user to being online on the shard:
89    ///
90    /// ```rust,no_run
91    /// # use serenity::prelude::*;
92    /// # use serenity::model::channel::Message;
93    /// #
94    /// # struct Handler;
95    ///
96    /// #[serenity::async_trait]
97    /// impl EventHandler for Handler {
98    ///     async fn message(&self, ctx: Context, msg: Message) {
99    ///         if msg.content == "!online" {
100    ///             ctx.online();
101    ///         }
102    ///     }
103    /// }
104    /// ```
105    ///
106    /// [`Online`]: OnlineStatus::Online
107    #[cfg(feature = "gateway")]
108    #[inline]
109    pub fn online(&self) {
110        self.shard.set_status(OnlineStatus::Online);
111    }
112
113    /// Sets the current user as being [`Idle`]. This maintains the current activity.
114    ///
115    /// # Examples
116    ///
117    /// Set the current user to being idle on the shard:
118    ///
119    /// ```rust,no_run
120    /// # use serenity::prelude::*;
121    /// # use serenity::model::channel::Message;
122    /// #
123    /// # struct Handler;
124    ///
125    /// #[serenity::async_trait]
126    /// impl EventHandler for Handler {
127    ///     async fn message(&self, ctx: Context, msg: Message) {
128    ///         if msg.content == "!idle" {
129    ///             ctx.idle();
130    ///         }
131    ///     }
132    /// }
133    /// ```
134    ///
135    /// [`Idle`]: OnlineStatus::Idle
136    #[cfg(feature = "gateway")]
137    #[inline]
138    pub fn idle(&self) {
139        self.shard.set_status(OnlineStatus::Idle);
140    }
141
142    /// Sets the current user as being [`DoNotDisturb`]. This maintains the current activity.
143    ///
144    /// # Examples
145    ///
146    /// Set the current user to being Do Not Disturb on the shard:
147    ///
148    /// ```rust,no_run
149    /// # use serenity::prelude::*;
150    /// # use serenity::model::channel::Message;
151    /// #
152    /// # struct Handler;
153    ///
154    /// #[serenity::async_trait]
155    /// impl EventHandler for Handler {
156    ///     async fn message(&self, ctx: Context, msg: Message) {
157    ///         if msg.content == "!dnd" {
158    ///             ctx.dnd();
159    ///         }
160    ///     }
161    /// }
162    /// ```
163    ///
164    /// [`DoNotDisturb`]: OnlineStatus::DoNotDisturb
165    #[cfg(feature = "gateway")]
166    #[inline]
167    pub fn dnd(&self) {
168        self.shard.set_status(OnlineStatus::DoNotDisturb);
169    }
170
171    /// Sets the current user as being [`Invisible`]. This maintains the current activity.
172    ///
173    /// # Examples
174    ///
175    /// Set the current user to being invisible on the shard:
176    ///
177    /// ```rust,no_run
178    /// # use serenity::prelude::*;
179    /// # use serenity::model::channel::Message;
180    /// #
181    /// # struct Handler;
182    ///
183    /// #[serenity::async_trait]
184    /// impl EventHandler for Handler {
185    ///     async fn message(&self, ctx: Context, msg: Message) {
186    ///         if msg.content == "!invisible" {
187    ///             ctx.invisible();
188    ///         }
189    ///     }
190    /// }
191    /// ```
192    ///
193    /// [`Invisible`]: OnlineStatus::Invisible
194    #[cfg(feature = "gateway")]
195    #[inline]
196    pub fn invisible(&self) {
197        self.shard.set_status(OnlineStatus::Invisible);
198    }
199
200    /// "Resets" the current user's presence, by setting the activity to [`None`] and the online
201    /// status to [`Online`].
202    ///
203    /// Use [`Self::set_presence`] for fine-grained control over individual details.
204    ///
205    /// # Examples
206    ///
207    /// Reset the current user's presence on the shard:
208    ///
209    /// ```rust,no_run
210    /// # use serenity::prelude::*;
211    /// # use serenity::model::channel::Message;
212    /// #
213    /// # struct Handler;
214    ///
215    /// #[serenity::async_trait]
216    /// impl EventHandler for Handler {
217    ///     async fn message(&self, ctx: Context, msg: Message) {
218    ///         if msg.content == "!reset_presence" {
219    ///             ctx.reset_presence();
220    ///         }
221    ///     }
222    /// }
223    /// ```
224    ///
225    /// [`Event::Resumed`]: crate::model::event::Event::Resumed
226    /// [`Online`]: OnlineStatus::Online
227    #[cfg(feature = "gateway")]
228    #[inline]
229    pub fn reset_presence(&self) {
230        self.shard.set_presence(None, OnlineStatus::Online);
231    }
232
233    /// Sets the current activity.
234    ///
235    /// # Examples
236    ///
237    /// Create a command named `~setgame` that accepts a name of a game to be playing:
238    ///
239    /// ```rust,no_run
240    /// # use serenity::prelude::*;
241    /// # use serenity::model::channel::Message;
242    /// # struct Handler;
243    ///
244    /// use serenity::gateway::ActivityData;
245    ///
246    /// #[serenity::async_trait]
247    /// impl EventHandler for Handler {
248    ///     async fn message(&self, ctx: Context, msg: Message) {
249    ///         let mut args = msg.content.splitn(2, ' ');
250    ///
251    ///         if let (Some("~setgame"), Some(game)) = (args.next(), args.next()) {
252    ///             ctx.set_activity(Some(ActivityData::playing(game)));
253    ///         }
254    ///     }
255    /// }
256    /// ```
257    #[cfg(feature = "gateway")]
258    #[inline]
259    pub fn set_activity(&self, activity: Option<ActivityData>) {
260        self.shard.set_activity(activity);
261    }
262
263    /// Sets the current user's presence, providing all fields to be passed.
264    ///
265    /// # Examples
266    ///
267    /// Setting the current user as having no activity and being [`Idle`]:
268    ///
269    /// ```rust,no_run
270    /// # use serenity::prelude::*;
271    /// # use serenity::model::gateway::Ready;
272    /// # struct Handler;
273    ///
274    /// #[serenity::async_trait]
275    /// impl EventHandler for Handler {
276    ///     async fn ready(&self, ctx: Context, _: Ready) {
277    ///         use serenity::model::user::OnlineStatus;
278    ///
279    ///         ctx.set_presence(None, OnlineStatus::Idle);
280    ///     }
281    /// }
282    /// ```
283    ///
284    /// Setting the current user as playing `"Heroes of the Storm"`, while being [`DoNotDisturb`]:
285    ///
286    /// ```rust,no_run
287    /// # use serenity::prelude::*;
288    /// # use serenity::model::gateway::Ready;
289    /// # struct Handler;
290    ///
291    /// #[serenity::async_trait]
292    /// impl EventHandler for Handler {
293    ///     async fn ready(&self, context: Context, _: Ready) {
294    ///         use serenity::gateway::ActivityData;
295    ///         use serenity::model::user::OnlineStatus;
296    ///
297    ///         let activity = ActivityData::playing("Heroes of the Storm");
298    ///         let status = OnlineStatus::DoNotDisturb;
299    ///
300    ///         context.set_presence(Some(activity), status);
301    ///     }
302    /// }
303    /// ```
304    ///
305    /// [`DoNotDisturb`]: OnlineStatus::DoNotDisturb
306    /// [`Idle`]: OnlineStatus::Idle
307    #[cfg(feature = "gateway")]
308    #[inline]
309    pub fn set_presence(&self, activity: Option<ActivityData>, status: OnlineStatus) {
310        self.shard.set_presence(activity, status);
311    }
312
313    /// Gets all emojis for the current application.
314    ///
315    /// # Errors
316    ///
317    /// Returns an error if the Application ID is not known.
318    pub async fn get_application_emojis(&self) -> Result<Vec<Emoji>> {
319        self.http.get_application_emojis().await
320    }
321
322    /// Gets information about an application emoji.
323    ///
324    /// # Errors
325    ///
326    /// Returns an error if the emoji does not exist.
327    pub async fn get_application_emoji(&self, emoji_id: EmojiId) -> Result<Emoji> {
328        self.http.get_application_emoji(emoji_id).await
329    }
330
331    /// Creates an application emoji with a name and base64-encoded image.
332    ///
333    /// # Errors
334    ///
335    /// See [`Guild::create_emoji`] for information about name and filesize requirements. This
336    /// method will error if said requirements are not met.
337    pub async fn create_application_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
338        #[derive(serde::Serialize)]
339        struct CreateEmoji<'a> {
340            name: &'a str,
341            image: &'a str,
342        }
343
344        let body = CreateEmoji {
345            name,
346            image,
347        };
348
349        self.http.create_application_emoji(&body).await
350    }
351
352    /// Changes the name of an application emoji.
353    ///
354    /// # Errors
355    ///
356    /// Returns an error if the emoji does not exist.
357    pub async fn edit_application_emoji(&self, emoji_id: EmojiId, name: &str) -> Result<Emoji> {
358        #[derive(serde::Serialize)]
359        struct EditEmoji<'a> {
360            name: &'a str,
361        }
362
363        let body = EditEmoji {
364            name,
365        };
366
367        self.http.edit_application_emoji(emoji_id, &body).await
368    }
369
370    /// Deletes an application emoji.
371    ///
372    /// # Errors
373    ///
374    /// Returns an error if the emoji does not exist.
375    pub async fn delete_application_emoji(&self, emoji_id: EmojiId) -> Result<()> {
376        self.http.delete_application_emoji(emoji_id).await
377    }
378}
379
380impl AsRef<Http> for Context {
381    fn as_ref(&self) -> &Http {
382        &self.http
383    }
384}
385
386impl AsRef<Http> for Arc<Context> {
387    fn as_ref(&self) -> &Http {
388        &self.http
389    }
390}
391
392impl AsRef<Arc<Http>> for Context {
393    fn as_ref(&self) -> &Arc<Http> {
394        &self.http
395    }
396}
397
398#[cfg(feature = "cache")]
399impl AsRef<Cache> for Context {
400    fn as_ref(&self) -> &Cache {
401        &self.cache
402    }
403}
404
405#[cfg(feature = "cache")]
406impl AsRef<Cache> for Arc<Context> {
407    fn as_ref(&self) -> &Cache {
408        &self.cache
409    }
410}
411
412#[cfg(feature = "cache")]
413impl AsRef<Arc<Cache>> for Context {
414    fn as_ref(&self) -> &Arc<Cache> {
415        &self.cache
416    }
417}
418
419#[cfg(feature = "cache")]
420impl AsRef<Cache> for Cache {
421    fn as_ref(&self) -> &Cache {
422        self
423    }
424}
425
426#[cfg(feature = "gateway")]
427impl AsRef<ShardMessenger> for Context {
428    fn as_ref(&self) -> &ShardMessenger {
429        &self.shard
430    }
431}