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}