aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2018-04-27 08:05:56 -0700
committerZeyla Hellyer <[email protected]>2018-05-27 19:26:44 -0700
commitbbff98c4c58f5169fa232cef2e714ba6b1490dfd (patch)
treee528ffeb883352c8584beac37b9636ba0e2612d6
parentRemove erroneous `migrations` directory (diff)
downloadarchived-serenity-bbff98c4c58f5169fa232cef2e714ba6b1490dfd.tar.xz
archived-serenity-bbff98c4c58f5169fa232cef2e714ba6b1490dfd.zip
Add Rich Presence parsing support
Adds support for parsing Rich Presences. This can not be used for setting activities with bots. Upgrade path: Basically change your import and usage from `serenity::model::gateway::Game` to `serenity::model::gateway::Activity`.
-rw-r--r--src/client/bridge/gateway/shard_messenger.rs26
-rw-r--r--src/client/bridge/gateway/shard_runner.rs16
-rw-r--r--src/client/bridge/gateway/shard_runner_message.rs10
-rw-r--r--src/client/context.rs62
-rw-r--r--src/gateway/mod.rs6
-rw-r--r--src/gateway/shard.rs18
-rw-r--r--src/gateway/ws_client_ext.rs4
-rw-r--r--src/internal/ws_impl.rs20
-rw-r--r--src/model/gateway.rs254
-rw-r--r--tests/resources/activity_1.json (renamed from tests/resources/game_1.json)0
-rw-r--r--tests/resources/activity_2.json26
-rw-r--r--tests/test_deser.rs13
12 files changed, 307 insertions, 148 deletions
diff --git a/src/client/bridge/gateway/shard_messenger.rs b/src/client/bridge/gateway/shard_messenger.rs
index 2331d4a..efec9fc 100644
--- a/src/client/bridge/gateway/shard_messenger.rs
+++ b/src/client/bridge/gateway/shard_messenger.rs
@@ -7,11 +7,11 @@ use websocket::message::OwnedMessage;
/// A lightweight wrapper around an mpsc sender.
///
/// This is used to cleanly communicate with a shard's respective
-/// [`ShardRunner`]. This can be used for actions such as setting the game via
-/// [`set_game`] or shutting down via [`shutdown`].
+/// [`ShardRunner`]. This can be used for actions such as setting the activity
+/// via [`set_activity`] or shutting down via [`shutdown`].
///
/// [`ShardRunner`]: struct.ShardRunner.html
-/// [`set_game`]: #method.set_game
+/// [`set_activity`]: #method.set_activity
/// [`shutdown`]: #method.shutdown
#[derive(Clone, Debug)]
pub struct ShardMessenger {
@@ -125,13 +125,13 @@ impl ShardMessenger {
});
}
- /// Sets the user's current game, if any.
+ /// Sets the user's current activity, if any.
///
/// Other presence settings are maintained.
///
/// # Examples
///
- /// Setting the current game to playing `"Heroes of the Storm"`:
+ /// Setting the current activity to playing `"Heroes of the Storm"`:
///
/// ```rust,no_run
/// # extern crate parking_lot;
@@ -147,9 +147,9 @@ impl ShardMessenger {
/// #
/// # let mut shard = Shard::new(mutex.clone(), mutex, [0, 1]).unwrap();
/// #
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
- /// shard.set_game(Some(Game::playing("Heroes of the Storm")));
+ /// shard.set_activity(Some(Activity::playing("Heroes of the Storm")));
/// # Ok(())
/// # }
/// #
@@ -157,8 +157,8 @@ impl ShardMessenger {
/// # try_main().unwrap();
/// # }
/// ```
- pub fn set_game(&self, game: Option<Game>) {
- let _ = self.send(ShardRunnerMessage::SetGame(game));
+ pub fn set_activity(&self, activity: Option<Activity>) {
+ let _ = self.send(ShardRunnerMessage::SetActivity(activity));
}
/// Sets the user's full presence information.
@@ -185,9 +185,9 @@ impl ShardMessenger {
/// #
/// # let mut shard = Shard::new(mutex.clone(), mutex, [0, 1]).unwrap();
/// #
- /// use serenity::model::{Game, OnlineStatus};
+ /// use serenity::model::{Activity, OnlineStatus};
///
- /// shard.set_presence(Some(Game::playing("Heroes of the Storm")), OnlineStatus::Online);
+ /// shard.set_presence(Some(Activity::playing("Heroes of the Storm")), OnlineStatus::Online);
/// # Ok(())
/// # }
/// #
@@ -195,12 +195,12 @@ impl ShardMessenger {
/// # try_main().unwrap();
/// # }
/// ```
- pub fn set_presence(&self, game: Option<Game>, mut status: OnlineStatus) {
+ pub fn set_presence(&self, activity: Option<Activity>, mut status: OnlineStatus) {
if status == OnlineStatus::Offline {
status = OnlineStatus::Invisible;
}
- let _ = self.send(ShardRunnerMessage::SetPresence(status, game));
+ let _ = self.send(ShardRunnerMessage::SetPresence(status, activity));
}
/// Sets the user's current online status.
diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs
index ea1fad6..0f244bd 100644
--- a/src/client/bridge/gateway/shard_runner.rs
+++ b/src/client/bridge/gateway/shard_runner.rs
@@ -258,26 +258,26 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> {
ShardRunnerMessage::Message(msg) => {
self.shard.client.send_message(&msg).is_ok()
},
- ShardRunnerMessage::SetGame(game) => {
- // To avoid a clone of `game`, we do a little bit of
+ ShardRunnerMessage::SetActivity(activity) => {
+ // To avoid a clone of `activity`, we do a little bit of
// trickery here:
//
// First, we obtain a reference to the current presence of
// the shard, and create a new presence tuple of the new
- // game we received over the channel as well as the online
- // status that the shard already had.
+ // activity we received over the channel as well as the
+ // online status that the shard already had.
//
// We then (attempt to) send the websocket message with the
// status update, expressively returning:
//
// - whether the message successfully sent
- // - the original game we received over the channel
- self.shard.set_game(game);
+ // - the original activity we received over the channel
+ self.shard.set_activity(activity);
self.shard.update_presence().is_ok()
},
- ShardRunnerMessage::SetPresence(status, game) => {
- self.shard.set_presence(status, game);
+ ShardRunnerMessage::SetPresence(status, activity) => {
+ self.shard.set_presence(status, activity);
self.shard.update_presence().is_ok()
},
diff --git a/src/client/bridge/gateway/shard_runner_message.rs b/src/client/bridge/gateway/shard_runner_message.rs
index 29024c9..2e262d8 100644
--- a/src/client/bridge/gateway/shard_runner_message.rs
+++ b/src/client/bridge/gateway/shard_runner_message.rs
@@ -1,7 +1,7 @@
use model::{
- gateway::Game,
+ gateway::Activity,
+ id::GuildId,
user::OnlineStatus,
- id::GuildId
};
use websocket::message::OwnedMessage;
@@ -38,11 +38,11 @@ pub enum ShardRunnerMessage {
Close(u16, Option<String>),
/// Indicates that the client is to send a custom WebSocket message.
Message(OwnedMessage),
- /// Indicates that the client is to update the shard's presence's game.
- SetGame(Option<Game>),
+ /// Indicates that the client is to update the shard's presence's activity.
+ SetActivity(Option<Activity>),
/// Indicates that the client is to update the shard's presence in its
/// entirity.
- SetPresence(OnlineStatus, Option<Game>),
+ SetPresence(OnlineStatus, Option<Activity>),
/// Indicates that the client is to update the shard's presence's status.
SetStatus(OnlineStatus),
}
diff --git a/src/client/context.rs b/src/client/context.rs
index 9bb6f68..72ca372 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -25,7 +25,7 @@ use utils::{self, VecMap};
/// [`Shard`] which received the event, or the low-level [`http`] module.
///
/// The context contains "shortcuts", like for interacting with the shard.
-/// Methods like [`set_game`] will unlock the shard and perform an update for
+/// Methods like [`set_activity`] will unlock the shard and perform an update for
/// you to save a bit of work.
///
/// A context will only live for the event it was dispatched for. After the
@@ -33,7 +33,7 @@ use utils::{self, VecMap};
///
/// [`Shard`]: ../gateway/struct.Shard.html
/// [`http`]: ../http/index.html
-/// [`set_game`]: #method.set_game
+/// [`set_activity`]: #method.set_activity
#[derive(Clone)]
pub struct Context {
/// A clone of [`Client::data`]. Refer to its documentation for more
@@ -121,7 +121,7 @@ impl Context {
/// Sets the current user as being [`Online`]. This maintains the current
- /// game.
+ /// activity.
///
/// # Examples
///
@@ -153,7 +153,7 @@ impl Context {
}
/// Sets the current user as being [`Idle`]. This maintains the current
- /// game.
+ /// activity.
///
/// # Examples
///
@@ -184,7 +184,7 @@ impl Context {
}
/// Sets the current user as being [`DoNotDisturb`]. This maintains the
- /// current game.
+ /// current activity.
///
/// # Examples
///
@@ -215,7 +215,7 @@ impl Context {
}
/// Sets the current user as being [`Invisible`]. This maintains the current
- /// game.
+ /// activity.
///
/// # Examples
///
@@ -246,8 +246,8 @@ impl Context {
self.shard.set_status(OnlineStatus::Invisible);
}
- /// "Resets" the current user's presence, by setting the game to `None` and
- /// the online status to [`Online`].
+ /// "Resets" the current user's presence, by setting the activity to `None`
+ /// and the online status to [`Online`].
///
/// Use [`set_presence`] for fine-grained control over individual details.
///
@@ -280,7 +280,7 @@ impl Context {
self.shard.set_presence(None, OnlineStatus::Online);
}
- /// Sets the current game, defaulting to an online status of [`Online`].
+ /// Sets the current activity, defaulting to an online status of [`Online`].
///
/// # Examples
///
@@ -293,7 +293,7 @@ impl Context {
/// # use serenity::prelude::*;
/// # use serenity::model::channel::Message;
/// #
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
/// struct Handler;
///
@@ -305,7 +305,7 @@ impl Context {
/// return;
/// }
///
- /// ctx.set_game(Game::playing(*unsafe { args.get_unchecked(1) }));
+ /// ctx.set_activity(Activity::playing(*unsafe { args.get_unchecked(1) }));
/// }
/// }
///
@@ -320,22 +320,22 @@ impl Context {
///
/// [`Online`]: ../model/user/enum.OnlineStatus.html#variant.Online
#[inline]
- pub fn set_game(&self, game: Game) {
- self.shard.set_presence(Some(game), OnlineStatus::Online);
+ pub fn set_activity(&self, activity: Activity) {
+ self.shard.set_presence(Some(activity), OnlineStatus::Online);
}
- /// Sets the current game, passing in only its name. This will automatically
- /// set the current user's [`OnlineStatus`] to [`Online`], and its
- /// [`GameType`] as [`Playing`].
+ /// Sets the current activity, passing in only its name. This will
+ /// automatically set the current user's [`OnlineStatus`] to [`Online`], and
+ /// its [`ActivityType`] as [`Playing`].
///
- /// Use [`reset_presence`] to clear the current game, or [`set_presence`]
- /// for more fine-grained control.
+ /// Use [`reset_presence`] to clear the current activity, or
+ /// [`set_presence`] for more fine-grained control.
///
/// **Note**: Maximum length is 128.
///
/// # Examples
///
- /// When an [`Event::Ready`] is received, set the game name to `"test"`:
+ /// When an [`Event::Ready`] is received, set the activity name to `"test"`:
///
/// ```rust,no_run
/// # use serenity::prelude::*;
@@ -354,27 +354,23 @@ impl Context {
/// ```
///
/// [`Event::Ready`]: ../model/event/enum.Event.html#variant.Ready
- /// [`GameType`]: ../model/gateway/enum.GameType.html
+ /// [`ActivityType`]: ../model/gateway/enum.ActivityType.html
/// [`Online`]: ../model/user/enum.OnlineStatus.html#variant.Online
/// [`OnlineStatus`]: ../model/user/enum.OnlineStatus.html
- /// [`Playing`]: ../model/gateway/enum.GameType.html#variant.Playing
+ /// [`Playing`]: ../model/gateway/enum.ActivityType.html#variant.Playing
/// [`reset_presence`]: #method.reset_presence
/// [`set_presence`]: #method.set_presence
pub fn set_game_name(&self, game_name: &str) {
- let game = Game {
- kind: GameType::Playing,
- name: game_name.to_string(),
- url: None,
- };
+ let activity = Activity::playing(game_name);
- self.shard.set_presence(Some(game), OnlineStatus::Online);
+ self.shard.set_presence(Some(activity), OnlineStatus::Online);
}
/// Sets the current user's presence, providing all fields to be passed.
///
/// # Examples
///
- /// Setting the current user as having no game and being [`Idle`]:
+ /// Setting the current user as having no activity and being [`Idle`]:
///
/// ```rust,no_run
/// # use serenity::prelude::*;
@@ -405,13 +401,13 @@ impl Context {
///
/// impl EventHandler for Handler {
/// fn ready(&self, context: Context, _: Ready) {
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
/// use serenity::model::user::OnlineStatus;
///
- /// let game = Game::playing("Heroes of the Storm");
+ /// let activity = Activity::playing("Heroes of the Storm");
/// let status = OnlineStatus::DoNotDisturb;
///
- /// context.set_presence(Some(game), status);
+ /// context.set_presence(Some(activity), status);
/// }
/// }
///
@@ -423,8 +419,8 @@ impl Context {
/// [`DoNotDisturb`]: ../model/user/enum.OnlineStatus.html#variant.DoNotDisturb
/// [`Idle`]: ../model/user/enum.OnlineStatus.html#variant.Idle
#[inline]
- pub fn set_presence(&self, game: Option<Game>, status: OnlineStatus) {
- self.shard.set_presence(game, status);
+ pub fn set_presence(&self, activity: Option<Activity>, status: OnlineStatus) {
+ self.shard.set_presence(activity, status);
}
/// Disconnects the shard from the websocket, essentially "quiting" it.
diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs
index b700491..5a64332 100644
--- a/src/gateway/mod.rs
+++ b/src/gateway/mod.rs
@@ -58,8 +58,8 @@ pub use self::{
};
use model::{
- gateway::Game,
- user::OnlineStatus
+ gateway::Activity,
+ user::OnlineStatus,
};
use serde_json::Value;
use std::fmt::{Display, Formatter, Result as FmtResult};
@@ -71,7 +71,7 @@ use websocket::sync::{
#[cfg(feature = "client")]
use client::bridge::gateway::ShardClientMessage;
-pub type CurrentPresence = (Option<Game>, OnlineStatus);
+pub type CurrentPresence = (Option<Activity>, OnlineStatus);
pub type WsClient = Client<TlsStream<TcpStream>>;
/// Indicates the current connection stage of a [`Shard`].
diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs
index 13944e6..6cd43e6 100644
--- a/src/gateway/shard.rs
+++ b/src/gateway/shard.rs
@@ -2,7 +2,7 @@ use constants::{self, close_codes};
use internal::prelude::*;
use model::{
event::{Event, GatewayEvent},
- gateway::Game,
+ gateway::Activity,
id::GuildId,
user::OnlineStatus
};
@@ -29,8 +29,8 @@ use websocket::{
/// A Shard is a higher-level handler for a websocket connection to Discord's
/// gateway. The shard allows for sending and receiving messages over the
-/// websocket, such as setting the active game, reconnecting, syncing guilds,
-/// and more.
+/// websocket, such as setting the active activity, reconnecting, syncing
+/// guilds, and more.
///
/// Refer to the [module-level documentation][module docs] for information on
/// effectively using multiple shards, if you need to.
@@ -272,22 +272,22 @@ impl Shard {
/// #
/// # let mut shard = Shard::new(mutex.clone(), mutex, [0, 1]).unwrap();
/// #
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
- /// shard.set_game(Some(Game::playing("Heroes of the Storm")));
+ /// shard.set_activity(Some(Activity::playing("Heroes of the Storm")));
/// # }
/// #
/// # #[cfg(not(feature = "model"))]
/// # fn main() { }
/// ```
#[inline]
- pub fn set_game(&mut self, game: Option<Game>) {
- self.current_presence.0 = game;
+ pub fn set_activity(&mut self, activity: Option<Activity>) {
+ self.current_presence.0 = activity;
}
#[inline]
- pub fn set_presence(&mut self, status: OnlineStatus, game: Option<Game>) {
- self.set_game(game);
+ pub fn set_presence(&mut self, status: OnlineStatus, activity: Option<Activity>) {
+ self.set_activity(activity);
self.set_status(status);
}
diff --git a/src/gateway/ws_client_ext.rs b/src/gateway/ws_client_ext.rs
index 934383c..27f11ee 100644
--- a/src/gateway/ws_client_ext.rs
+++ b/src/gateway/ws_client_ext.rs
@@ -92,7 +92,7 @@ impl WebSocketGatewayClientExt for WsClient {
shard_info: &[u64; 2],
current_presence: &CurrentPresence,
) -> Result<()> {
- let &(ref game, ref status) = current_presence;
+ let &(ref activity, ref status) = current_presence;
let now = Utc::now().timestamp() as u64;
debug!("[Shard {:?}] Sending presence update", shard_info);
@@ -103,7 +103,7 @@ impl WebSocketGatewayClientExt for WsClient {
"afk": false,
"since": now,
"status": status.name(),
- "game": game.as_ref().map(|x| json!({
+ "game": activity.as_ref().map(|x| json!({
"name": x.name,
"type": x.kind,
"url": x.url,
diff --git a/src/internal/ws_impl.rs b/src/internal/ws_impl.rs
index 5d8969b..fbf6ae4 100644
--- a/src/internal/ws_impl.rs
+++ b/src/internal/ws_impl.rs
@@ -20,25 +20,13 @@ impl ReceiverExt for WsClient<TlsStream<TcpStream>> {
fn recv_json(&mut self) -> Result<Option<Value>> {
Ok(match self.recv_message()? {
OwnedMessage::Binary(bytes) => {
- serde_json::from_reader(ZlibDecoder::new(&bytes[..]))
- .map(Some)
- .map_err(|why| {
- warn!("Err deserializing bytes: {:?}; bytes: {:?}", why, bytes);
-
- why
- })?
+ println!("{:?}", bytes);
+ serde_json::from_reader(ZlibDecoder::new(&bytes[..])).map(Some)?
},
OwnedMessage::Close(data) => return Err(Error::Gateway(GatewayError::Closed(data))),
OwnedMessage::Text(payload) => {
- serde_json::from_str(&payload).map(Some).map_err(|why| {
- warn!(
- "Err deserializing text: {:?}; text: {}",
- why,
- payload,
- );
-
- why
- })?
+ println!("{}", payload);
+ serde_json::from_str(&payload).map(Some)?
},
OwnedMessage::Ping(x) => {
self.send_message(&OwnedMessage::Pong(x))
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index e795077..1584633 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -1,5 +1,6 @@
//! Models pertaining to the gateway.
+use chrono::{DateTime, Utc};
use parking_lot::RwLock;
use serde::de::Error as DeError;
use serde::ser::{SerializeStruct, Serialize, Serializer};
@@ -23,80 +24,116 @@ pub struct BotGateway {
pub url: String,
}
-/// Representation of a game that a [`User`] is playing -- or streaming in the
-/// case that a stream URL is provided.
+/// Representation of an activity that a [`User`] is performing.
#[derive(Clone, Debug, Serialize)]
-pub struct Game {
- /// The type of game status.
- #[serde(default, rename = "type")]
- pub kind: GameType,
- /// The name of the game being played.
+pub struct Activity {
+ /// The ID of the application for the activity.
+ pub application_id: Option<ApplicationId>,
+ /// Images for the presence and their texts.
+ pub assets: Option<ActivityAssets>,
+ /// What the user is doing.
+ pub details: Option<String>,
+ /// Activity flags describing what the payload includes.
+ pub flags: Option<ActivityFlags>,
+ /// Whether or not the activity is an instanced game session.
+ pub instance: Option<bool>,
+ /// The type of activity being performed
+ #[serde(default = "ActivityType::default", rename = "type")]
+ pub kind: ActivityType,
+ /// The name of the activity.
pub name: String,
- /// The Stream URL if [`kind`] is [`GameType::Streaming`].
+ /// Information about the user's current party.
+ pub party: Option<ActivityParty>,
+ /// Secrets for Rich Presence joining and spectating.
+ pub secrets: Option<ActivitySecrets>,
+ /// The user's current party status.
+ pub state: Option<String>,
+ /// Unix timestamps for the start and/or end times of the activity.
+ pub timestamps: Option<ActivityTimestamps>,
+ /// The Stream URL if [`kind`] is [`ActivityType::Streaming`].
///
- /// [`GameType::Streaming`]: enum.GameType.html#variant.Streaming
+ /// [`ActivityType::Streaming`]: enum.ActivityType.html#variant.Streaming
/// [`kind`]: #structfield.kind
pub url: Option<String>,
}
#[cfg(feature = "model")]
-impl Game {
+impl Activity {
/// Creates a `Game` struct that appears as a `Playing <name>` status.
///
/// **Note**: Maximum `name` length is 128.
///
/// # Examples
///
- /// Create a command that sets the current game being played:
+ /// Create a command that sets the current activity:
///
/// ```rust,no_run
/// # #[macro_use] extern crate serenity;
/// #
/// use serenity::framework::standard::Args;
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
- /// command!(game(ctx, _msg, args) {
+ /// command!(activity(ctx, _msg, args) {
/// let name = args.full();
- /// ctx.set_game(Game::playing(&name));
+ /// ctx.set_activity(Activity::playing(&name));
/// });
/// #
/// # fn main() {}
/// ```
- pub fn playing(name: &str) -> Game {
- Game {
- kind: GameType::Playing,
+ pub fn playing(name: &str) -> Activity {
+ Activity {
+ application_id: None,
+ assets: None,
+ details: None,
+ flags: None,
+ instance: None,
+ kind: ActivityType::Playing,
name: name.to_string(),
+ party: None,
+ secrets: None,
+ state: None,
+ timestamps: None,
url: None,
}
}
- /// Creates a `Game` struct that appears as a `Streaming <name>` status.
+ /// Creates an `Activity` struct that appears as a `Streaming <name>`
+ /// status.
///
/// **Note**: Maximum `name` length is 128.
///
/// # Examples
///
- /// Create a command that sets the current game and stream:
+ /// Create a command that sets the current streaming status:
///
/// ```rust,no_run
/// # #[macro_use] extern crate serenity;
/// #
/// use serenity::framework::standard::Args;
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
/// // Assumes command has min_args set to 2.
/// command!(stream(ctx, _msg, args) {
/// # let stream_url = String::from("");
/// let name = args.full();
- /// ctx.set_game(Game::streaming(&name, &stream_url));
+ /// ctx.set_activity(Activity::streaming(&name, &stream_url));
/// });
/// #
/// # fn main() {}
/// ```
- pub fn streaming(name: &str, url: &str) -> Game {
- Game {
- kind: GameType::Streaming,
+ pub fn streaming(name: &str, url: &str) -> Activity {
+ Activity {
+ application_id: None,
+ assets: None,
+ details: None,
+ flags: None,
+ instance: None,
+ kind: ActivityType::Streaming,
name: name.to_string(),
+ party: None,
+ secrets: None,
+ state: None,
+ timestamps: None,
url: Some(url.to_string()),
}
}
@@ -107,55 +144,159 @@ impl Game {
///
/// # Examples
///
- /// Create a command that sets the current game being played:
+ /// Create a command that sets the current listening status:
///
/// ```rust,no_run
/// # #[macro_use] extern crate serenity;
/// #
/// use serenity::framework::standard::Args;
- /// use serenity::model::gateway::Game;
+ /// use serenity::model::gateway::Activity;
///
/// command!(listen(ctx, _msg, args) {
/// let name = args.full();
- /// ctx.set_game(Game::listening(&name));
+ /// ctx.set_activity(Activity::listening(&name));
/// });
/// #
/// # fn main() {}
/// ```
- pub fn listening(name: &str) -> Game {
- Game {
- kind: GameType::Listening,
+ pub fn listening(name: &str) -> Activity {
+ Activity {
+ application_id: None,
+ assets: None,
+ details: None,
+ flags: None,
+ instance: None,
+ kind: ActivityType::Listening,
name: name.to_string(),
+ party: None,
+ secrets: None,
+ state: None,
+ timestamps: None,
url: None,
}
}
}
-impl<'de> Deserialize<'de> for Game {
+impl<'de> Deserialize<'de> for Activity {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
+ let application_id = match map.remove("application_id") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let assets = match map.remove("assets") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let details = match map.remove("details") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let flags = match map.remove("flags") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let instance = match map.remove("instance") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
let kind = map.remove("type")
- .and_then(|v| GameType::deserialize(v).ok())
- .unwrap_or(GameType::Playing);
+ .and_then(|v| ActivityType::deserialize(v).ok())
+ .unwrap_or(ActivityType::Playing);
let name = map.remove("name")
.and_then(|v| String::deserialize(v).ok())
.unwrap_or_else(String::new);
+ let party = match map.remove("party") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let secrets = match map.remove("secrets") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let state = match map.remove("state") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
+ let timestamps = match map.remove("timestamps") {
+ Some(v) => serde_json::from_value::<Option<_>>(v).map_err(DeError::custom)?,
+ None => None,
+ };
let url = map.remove("url")
.and_then(|v| serde_json::from_value::<String>(v).ok());
- Ok(Game {
+ Ok(Activity {
+ application_id,
+ assets,
+ details,
+ flags,
+ instance,
kind,
name,
+ party,
+ secrets,
+ state,
+ timestamps,
url,
})
}
}
+/// The assets for an activity.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ActivityAssets {
+ /// The ID for a large asset of the activity, usually a snowflake.
+ pub large_image: Option<String>,
+ /// Text displayed when hovering over the large image of the activity.
+ pub large_text: Option<String>,
+ /// The ID for a small asset of the activity, usually a snowflake.
+ pub small_image: Option<String>,
+ /// Text displayed when hovering over the small image of the activity.
+ pub small_text: Option<String>,
+}
+bitflags! {
+ /// A set of flags defining what is in an activity's payload.
+ #[derive(Deserialize, Serialize)]
+ pub struct ActivityFlags: u64 {
+ /// Whether the activity is an instance activity.
+ const INSTANCE = 0b001;
+ /// Whether the activity is joinable.
+ const JOIN = 0b010;
+ /// Whether the activity can be spectated.
+ const SPECTATE = 0b011;
+ /// Whether a request can be sent to join the user's party.
+ const JOIN_REQUEST = 0b100;
+ /// Whether the activity can be synced.
+ const SYNC = 0b101;
+ /// Whether the activity can be played.
+ const PLAY = 0b110;
+ }
+}
-/// The type of activity that is being performed when playing a game.
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
-pub enum GameType {
+/// Information about an activity's party.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ActivityParty {
+ /// The ID of the party.
+ pub id: Option<String>,
+ /// Used to show the party's current and maximum size.
+ pub size: [u64; 2],
+}
+
+/// Secrets for an activity.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ActivitySecrets {
+ /// The secret for joining a party.
+ pub join: Option<String>,
+ /// The secret for a specific instanced match.
+ #[serde(rename = "match")]
+ pub match_: Option<String>,
+ /// The secret for spectating an activity.
+ pub spectate: Option<String>,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ActivityType {
/// An indicator that the user is playing a game.
Playing = 0,
/// An indicator that the user is streaming to a service.
@@ -165,16 +306,16 @@ pub enum GameType {
}
enum_number!(
- GameType {
+ ActivityType {
Playing,
Streaming,
Listening,
}
);
-impl GameType {
+impl ActivityType {
pub fn num(&self) -> u64 {
- use self::GameType::*;
+ use self::ActivityType::*;
match *self {
Playing => 0,
@@ -184,8 +325,8 @@ impl GameType {
}
}
-impl Default for GameType {
- fn default() -> Self { GameType::Playing }
+impl Default for ActivityType {
+ fn default() -> Self { ActivityType::Playing }
}
/// A representation of the data retrieved from the gateway endpoint.
@@ -204,10 +345,10 @@ pub struct Gateway {
/// [`User`]: struct.User.html
#[derive(Clone, Debug)]
pub struct Presence {
- /// The game that a [`User`] is current playing.
+ /// The activity that a [`User`] is performing.
///
/// [`User`]: struct.User.html
- pub game: Option<Game>,
+ pub activity: Option<Activity>,
/// The date of the last presence update.
pub last_modified: Option<u64>,
/// The nickname of the member, if applicable.
@@ -244,8 +385,8 @@ impl<'de> Deserialize<'de> for Presence {
(user_id, None)
};
- let game = match map.remove("game") {
- Some(v) => serde_json::from_value::<Option<Game>>(v)
+ let activity = match map.remove("game") {
+ Some(v) => serde_json::from_value::<Option<Activity>>(v)
.map_err(DeError::custom)?,
None => None,
};
@@ -264,12 +405,12 @@ impl<'de> Deserialize<'de> for Presence {
.map_err(DeError::custom)?;
Ok(Presence {
- game,
- last_modified,
- nick,
- status,
- user,
- user_id,
+ activity: activity,
+ last_modified: last_modified,
+ nick: nick,
+ status: status,
+ user: user,
+ user_id: user_id,
})
}
}
@@ -283,7 +424,7 @@ impl Serialize for Presence {
}
let mut state = serializer.serialize_struct("Presence", 5)?;
- state.serialize_field("game", &self.game)?;
+ state.serialize_field("game", &self.activity)?;
state.serialize_field("last_modified", &self.last_modified)?;
state.serialize_field("nick", &self.nick)?;
state.serialize_field("status", &self.status)?;
@@ -316,3 +457,10 @@ pub struct Ready {
#[serde(rename = "v")]
pub version: u64,
}
+
+/// Timestamps of when a user started and/or is ending their activity.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct ActivityTimestamps {
+ pub end: Option<DateTime<Utc>>,
+ pub start: Option<DateTime<Utc>>,
+}
diff --git a/tests/resources/game_1.json b/tests/resources/activity_1.json
index 4e8a4b4..4e8a4b4 100644
--- a/tests/resources/game_1.json
+++ b/tests/resources/activity_1.json
diff --git a/tests/resources/activity_2.json b/tests/resources/activity_2.json
new file mode 100644
index 0000000..12b0d4f
--- /dev/null
+++ b/tests/resources/activity_2.json
@@ -0,0 +1,26 @@
+{
+ "user": {
+ "id": "210141176211177474"
+ },
+ "status": "idle",
+ "game": {
+ "type": 2,
+ "timestamps": {
+ "start": 1524840854122,
+ "end": 1524841056674
+ },
+ "sync_id": "3MzqPLieWzTj5NW6WEzICu",
+ "state": "I Fight Dragons",
+ "session_id": "f737b052de95d8199723b92c1457463e",
+ "party": {
+ "id": "spotify:210141176211177474"
+ },
+ "name": "Spotify",
+ "flags": 48,
+ "details": "Pretend",
+ "assets": {
+ "large_text": "The Near Future",
+ "large_image": "spotify:a253f70af71e454b1021f8323357f6f983d622ed"
+ }
+ }
+}
diff --git a/tests/test_deser.rs b/tests/test_deser.rs
index 70b6f1f..af50e5a 100644
--- a/tests/test_deser.rs
+++ b/tests/test_deser.rs
@@ -15,6 +15,13 @@ macro_rules! p {
};
}
+// An activity with null type.
+#[test]
+fn activity() {
+ p!(Activity, "activity_1");
+ p!(Activity, "activity_2");
+}
+
#[test]
fn channel_create() {
p!(ChannelCreateEvent, "channel_create_1");
@@ -40,12 +47,6 @@ fn emoji_animated() {
p!(Emoji, "emoji_animated");
}
-// A game with null type.
-#[test]
-fn game() {
- p!(Game, "game_1");
-}
-
#[test]
fn guild_ban_add() {
p!(GuildBanAddEvent, "guild_ban_add_1");