Skip to content

Commit

Permalink
Rework setting and storing of presence data (serenity-rs#2007)
Browse files Browse the repository at this point in the history
  • Loading branch information
vaporoxx authored and mkrasnitski committed Sep 21, 2023
1 parent f78f98e commit f33ff7a
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 239 deletions.
9 changes: 5 additions & 4 deletions examples/e13_parallel_loops/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use std::time::Duration;

use chrono::offset::Utc;
use serenity::async_trait;
use serenity::gateway::ActivityData;
use serenity::model::channel::Message;
use serenity::model::gateway::{Activity, Ready};
use serenity::model::gateway::Ready;
use serenity::model::id::{ChannelId, GuildId};
use serenity::prelude::*;

Expand Down Expand Up @@ -61,7 +62,7 @@ impl EventHandler for Handler {
let ctx2 = Arc::clone(&ctx);
tokio::spawn(async move {
loop {
set_status_to_current_time(Arc::clone(&ctx2)).await;
set_activity_to_current_time(Arc::clone(&ctx2)).await;
tokio::time::sleep(Duration::from_secs(60)).await;
}
});
Expand Down Expand Up @@ -100,11 +101,11 @@ async fn log_system_load(ctx: Arc<Context>) {
};
}

async fn set_status_to_current_time(ctx: Arc<Context>) {
async fn set_activity_to_current_time(ctx: Arc<Context>) {
let current_time = Utc::now();
let formatted_time = current_time.to_rfc2822();

ctx.set_activity(Activity::playing(formatted_time)).await;
ctx.set_activity(Some(ActivityData::playing(formatted_time))).await;
}

#[tokio::main]
Expand Down
16 changes: 8 additions & 8 deletions src/client/bridge/gateway/shard_messenger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::collector::{
ModalInteractionFilter,
ReactionFilter,
};
use crate::gateway::InterMessage;
use crate::gateway::{ActivityData, InterMessage};
use crate::model::prelude::*;

/// A lightweight wrapper around an mpsc sender.
Expand Down Expand Up @@ -149,13 +149,13 @@ impl ShardMessenger {
/// # };
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", shard_info, GatewayIntents::all()).await?;
/// use serenity::model::gateway::Activity;
/// use serenity::gateway::ActivityData;
///
/// shard.set_activity(Some(Activity::playing("Heroes of the Storm")));
/// shard.set_activity(Some(ActivityData::playing("Heroes of the Storm")));
/// # Ok(())
/// # }
/// ```
pub fn set_activity(&self, activity: Option<Activity>) {
pub fn set_activity(&self, activity: Option<ActivityData>) {
drop(self.send_to_shard(ShardRunnerMessage::SetActivity(activity)));
}

Expand Down Expand Up @@ -184,20 +184,20 @@ impl ShardMessenger {
/// #
/// # let mut shard = Shard::new(mutex.clone(), "", shard_info, None).await?;
/// #
/// use serenity::model::gateway::Activity;
/// use serenity::gateway::ActivityData;
/// use serenity::model::user::OnlineStatus;
///
/// let activity = Activity::playing("Heroes of the Storm");
/// let activity = ActivityData::playing("Heroes of the Storm");
/// shard.set_presence(Some(activity), OnlineStatus::Online);
/// # Ok(())
/// # }
/// ```
pub fn set_presence(&self, activity: Option<Activity>, mut status: OnlineStatus) {
pub fn set_presence(&self, activity: Option<ActivityData>, mut status: OnlineStatus) {
if status == OnlineStatus::Offline {
status = OnlineStatus::Invisible;
}

drop(self.send_to_shard(ShardRunnerMessage::SetPresence(status, activity)));
drop(self.send_to_shard(ShardRunnerMessage::SetPresence(activity, status)));
}

/// Sets the user's current online status.
Expand Down
4 changes: 2 additions & 2 deletions src/client/bridge/gateway/shard_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,8 @@ impl ShardRunner {
self.shard.set_activity(activity);
self.shard.update_presence().await.is_ok()
},
ShardRunnerMessage::SetPresence(status, activity) => {
self.shard.set_presence(status, activity);
ShardRunnerMessage::SetPresence(activity, status) => {
self.shard.set_presence(activity, status);
self.shard.update_presence().await.is_ok()
},
ShardRunnerMessage::SetStatus(status) => {
Expand Down
6 changes: 3 additions & 3 deletions src/client/bridge/gateway/shard_runner_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::collector::{
ModalInteractionFilter,
ReactionFilter,
};
use crate::model::gateway::Activity;
use crate::gateway::ActivityData;
use crate::model::id::{GuildId, UserId};
use crate::model::user::OnlineStatus;

Expand Down Expand Up @@ -55,10 +55,10 @@ pub enum ShardRunnerMessage {
/// Indicates that the client is to send a custom WebSocket message.
Message(Message),
/// Indicates that the client is to update the shard's presence's activity.
SetActivity(Option<Activity>),
SetActivity(Option<ActivityData>),
/// Indicates that the client is to update the shard's presence in its
/// entirety.
SetPresence(OnlineStatus, Option<Activity>),
SetPresence(Option<ActivityData>, OnlineStatus),
/// Indicates that the client is to update the shard's presence's status.
SetStatus(OnlineStatus),
/// Sends a new filter for events to the shard.
Expand Down
21 changes: 10 additions & 11 deletions src/client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use crate::cache::Cache;
use crate::client::bridge::gateway::ShardMessenger;
#[cfg(feature = "collector")]
use crate::collector::{ComponentInteractionFilter, MessageFilter, ReactionFilter};
use crate::gateway::ActivityData;
#[cfg(feature = "gateway")]
use crate::gateway::InterMessage;
use crate::http::Http;
Expand Down Expand Up @@ -282,10 +283,10 @@ impl Context {
#[allow(clippy::unused_async)]
#[inline]
pub async fn reset_presence(&self) {
self.shard.set_presence(None::<Activity>, OnlineStatus::Online);
self.shard.set_presence(None, OnlineStatus::Online);
}

/// Sets the current activity, defaulting to an online status of [`Online`].
/// Sets the current activity.
///
/// # Examples
///
Expand All @@ -296,7 +297,7 @@ impl Context {
/// # use serenity::prelude::*;
/// # use serenity::model::channel::Message;
/// #
/// use serenity::model::gateway::Activity;
/// use serenity::gateway::ActivityData;
///
/// struct Handler;
///
Expand All @@ -306,7 +307,7 @@ impl Context {
/// let mut args = msg.content.splitn(2, ' ');
///
/// if let (Some("~setgame"), Some(game)) = (args.next(), args.next()) {
/// ctx.set_activity(Activity::playing(game)).await;
/// ctx.set_activity(Some(ActivityData::playing(game))).await;
/// }
/// }
/// }
Expand All @@ -319,13 +320,11 @@ impl Context {
/// # Ok(())
/// # }
/// ```
///
/// [`Online`]: OnlineStatus::Online
#[cfg(feature = "gateway")]
#[allow(clippy::unused_async)]
#[inline]
pub async fn set_activity(&self, activity: Activity) {
self.shard.set_presence(Some(activity), OnlineStatus::Online);
pub async fn set_activity(&self, activity: Option<ActivityData>) {
self.shard.set_activity(activity);
}

/// Sets the current user's presence, providing all fields to be passed.
Expand Down Expand Up @@ -370,10 +369,10 @@ impl Context {
/// #[serenity::async_trait]
/// impl EventHandler for Handler {
/// async fn ready(&self, context: Context, _: Ready) {
/// use serenity::model::gateway::Activity;
/// use serenity::gateway::ActivityData;
/// use serenity::model::user::OnlineStatus;
///
/// let activity = Activity::playing("Heroes of the Storm");
/// let activity = ActivityData::playing("Heroes of the Storm");
/// let status = OnlineStatus::DoNotDisturb;
///
/// context.set_presence(Some(activity), status);
Expand All @@ -394,7 +393,7 @@ impl Context {
#[cfg(feature = "gateway")]
#[allow(clippy::unused_async)]
#[inline]
pub async fn set_presence(&self, activity: Option<Activity>, status: OnlineStatus) {
pub async fn set_presence(&self, activity: Option<ActivityData>, status: OnlineStatus) {
self.shard.set_presence(activity, status);
}

Expand Down
92 changes: 90 additions & 2 deletions src/gateway/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,104 @@ mod ws;

use std::fmt;

use reqwest::{IntoUrl, Url};

pub use self::error::Error as GatewayError;
pub use self::shard::Shard;
pub use self::ws::WsClient;
#[cfg(feature = "client")]
use crate::client::bridge::gateway::ShardClientMessage;
use crate::internal::prelude::*;
use crate::json::Value;
use crate::model::gateway::Activity;
use crate::model::gateway::{Activity, ActivityType};
use crate::model::user::OnlineStatus;

pub type CurrentPresence = (Option<Activity>, OnlineStatus);
/// Presence data of the current user.
#[derive(Clone, Debug, Default)]
pub struct PresenceData {
/// The current activity, if present
pub activity: Option<ActivityData>,
/// The current online status
pub status: OnlineStatus,
}

/// Activity data of the current user.
#[derive(Clone, Debug, Serialize)]
pub struct ActivityData {
/// The name of the activity
pub name: String,
/// The type of the activity
#[serde(rename = "type")]
pub kind: ActivityType,
/// The url of the activity, if the type is [`ActivityType::Streaming`]
pub url: Option<Url>,
}

impl ActivityData {
/// Creates an activity that appears as `Playing <name>`.
#[must_use]
pub fn playing(name: impl Into<String>) -> Self {
Self {
name: name.into(),
kind: ActivityType::Playing,
url: None,
}
}

/// Creates an activity that appears as `Streaming <name>`.
///
/// # Errors
///
/// Returns an error if the URL parsing fails.
#[must_use]
pub fn streaming(name: impl Into<String>, url: impl IntoUrl) -> Result<Self> {
Ok(Self {
name: name.into(),
kind: ActivityType::Streaming,
url: Some(url.into_url()?),
})
}

/// Creates an activity that appears as `Listening to <name>`.
#[must_use]
pub fn listening(name: impl Into<String>) -> Self {
Self {
name: name.into(),
kind: ActivityType::Listening,
url: None,
}
}

/// Creates an activity that appears as `Watching <name>`.
#[must_use]
pub fn watching(name: impl Into<String>) -> Self {
Self {
name: name.into(),
kind: ActivityType::Watching,
url: None,
}
}

/// Creates an activity that appears as `Competing in <name>`.
#[must_use]
pub fn competing(name: impl Into<String>) -> Self {
Self {
name: name.into(),
kind: ActivityType::Competing,
url: None,
}
}
}

impl From<Activity> for ActivityData {
fn from(activity: Activity) -> Self {
Self {
name: activity.name,
kind: activity.kind,
url: activity.url,
}
}
}

/// Indicates the current connection stage of a [`Shard`].
///
Expand Down
32 changes: 20 additions & 12 deletions src/gateway/shard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ use tokio::sync::Mutex;
use tracing::{debug, error, info, instrument, trace, warn};
use url::Url;

use super::{ConnectionStage, CurrentPresence, GatewayError, ReconnectType, ShardAction, WsClient};
use super::{
ActivityData,
ConnectionStage,
GatewayError,
PresenceData,
ReconnectType,
ShardAction,
WsClient,
};
use crate::client::bridge::gateway::ChunkGuildFilter;
use crate::constants::{self, close_codes};
use crate::http::Http;
use crate::internal::prelude::*;
use crate::model::event::{Event, GatewayEvent};
use crate::model::gateway::{Activity, GatewayIntents, ShardInfo};
use crate::model::gateway::{GatewayIntents, ShardInfo};
use crate::model::id::GuildId;
use crate::model::user::OnlineStatus;

Expand Down Expand Up @@ -51,7 +59,7 @@ use crate::model::user::OnlineStatus;
/// [module docs]: crate::gateway#sharding
pub struct Shard {
pub client: WsClient,
current_presence: CurrentPresence,
presence: PresenceData,
/// A tuple of:
///
/// - the last instant that a heartbeat was sent
Expand Down Expand Up @@ -131,7 +139,7 @@ impl Shard {
let url = ws_url.lock().await.clone();
let client = connect(&url).await?;

let current_presence = (None, OnlineStatus::Online);
let presence = PresenceData::default();
let heartbeat_instants = (None, None);
let heartbeat_interval = None;
let last_heartbeat_acknowledged = true;
Expand All @@ -141,7 +149,7 @@ impl Shard {

Ok(Shard {
client,
current_presence,
presence,
heartbeat_instants,
heartbeat_interval,
http: None,
Expand All @@ -166,8 +174,8 @@ impl Shard {

/// Retrieves the current presence of the shard.
#[inline]
pub fn current_presence(&self) -> &CurrentPresence {
&self.current_presence
pub fn presence(&self) -> &PresenceData {
&self.presence
}

/// Retrieves the heartbeat instants of the shard.
Expand Down Expand Up @@ -248,13 +256,13 @@ impl Shard {

#[inline]
#[instrument(skip(self))]
pub fn set_activity(&mut self, activity: Option<Activity>) {
self.current_presence.0 = activity;
pub fn set_activity(&mut self, activity: Option<ActivityData>) {
self.presence.activity = activity;
}

#[inline]
#[instrument(skip(self))]
pub fn set_presence(&mut self, status: OnlineStatus, activity: Option<Activity>) {
pub fn set_presence(&mut self, activity: Option<ActivityData>, status: OnlineStatus) {
self.set_activity(activity);
self.set_status(status);
}
Expand All @@ -266,7 +274,7 @@ impl Shard {
status = OnlineStatus::Invisible;
}

self.current_presence.1 = status;
self.presence.status = status;
}

/// Retrieves a copy of the current shard information.
Expand Down Expand Up @@ -772,7 +780,7 @@ impl Shard {

#[instrument(skip(self))]
pub async fn update_presence(&mut self) -> Result<()> {
self.client.send_presence_update(&self.shard_info, &self.current_presence).await
self.client.send_presence_update(&self.shard_info, &self.presence).await
}
}

Expand Down
Loading

0 comments on commit f33ff7a

Please sign in to comment.