aboutsummaryrefslogtreecommitdiff
path: root/src/model
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 /src/model
parentRemove erroneous `migrations` directory (diff)
downloadserenity-bbff98c4c58f5169fa232cef2e714ba6b1490dfd.tar.xz
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`.
Diffstat (limited to 'src/model')
-rw-r--r--src/model/gateway.rs254
1 files changed, 201 insertions, 53 deletions
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>>,
+}