diff options
| author | Austin Hellyer <[email protected]> | 2016-09-19 09:00:03 -0700 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2016-10-18 11:14:27 -0700 |
| commit | 8fc8c81403c3daa187ba96a7d488a64db21463bf (patch) | |
| tree | 81bc4890c28b08ce806f69084617066bce863c2d /src/model/gateway.rs | |
| download | serenity-8fc8c81403c3daa187ba96a7d488a64db21463bf.tar.xz serenity-8fc8c81403c3daa187ba96a7d488a64db21463bf.zip | |
Initial commit
Diffstat (limited to 'src/model/gateway.rs')
| -rw-r--r-- | src/model/gateway.rs | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/src/model/gateway.rs b/src/model/gateway.rs new file mode 100644 index 0000000..2e1b300 --- /dev/null +++ b/src/model/gateway.rs @@ -0,0 +1,784 @@ +use std::collections::{BTreeMap, HashMap}; +use super::utils::*; +use super::*; +use ::prelude::*; +use ::utils::decode_array; + +#[derive(Clone, Debug)] +pub struct CallCreateEvent { + pub call: Call, +} + +#[derive(Clone, Debug)] +pub struct CallDeleteEvent { + pub channel_id: ChannelId, +} + +#[derive(Clone, Debug)] +pub struct CallUpdateEvent { + pub channel_id: ChannelId, + pub message_id: MessageId, + pub region: String, + pub ringing: Vec<UserId>, +} + +#[derive(Clone, Debug)] +pub struct ChannelCreateEvent { + pub channel: Channel, +} + +#[derive(Clone, Debug)] +pub struct ChannelDeleteEvent { + pub channel: Channel, +} + +#[derive(Clone, Debug)] +pub struct ChannelPinsAckEvent { + pub channel_id: ChannelId, + pub timestamp: String, +} + +#[derive(Clone, Debug)] +pub struct ChannelPinsUpdateEvent { + pub channel_id: ChannelId, + pub last_pin_timestamp: Option<String>, +} + +#[derive(Clone, Debug)] +pub struct ChannelRecipientAddEvent { + pub channel_id: ChannelId, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct ChannelRecipientRemoveEvent { + pub channel_id: ChannelId, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct ChannelUpdateEvent { + pub channel: Channel, +} + +#[derive(Clone, Debug)] +pub struct GuildBanAddEvent { + pub guild_id: GuildId, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct GuildBanRemoveEvent { + pub guild_id: GuildId, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct GuildCreateEvent { + pub guild: LiveGuild, +} + +#[derive(Clone, Debug)] +pub struct GuildDeleteEvent { + pub guild: Guild, +} + +#[derive(Clone, Debug)] +pub struct GuildEmojisUpdateEvent { + pub emojis: HashMap<EmojiId, Emoji>, + pub guild_id: GuildId, +} + +#[derive(Clone, Debug)] +pub struct GuildIntegrationsUpdateEvent { + pub guild_id: GuildId, +} + +#[derive(Clone, Debug)] +pub struct GuildMemberAddEvent { + pub guild_id: GuildId, + pub member: Member, +} + +#[derive(Clone, Debug)] +pub struct GuildMemberRemoveEvent { + pub guild_id: GuildId, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct GuildMemberUpdateEvent { + pub guild_id: GuildId, + pub nick: Option<String>, + pub roles: Vec<RoleId>, + pub user: User, +} + +#[derive(Clone, Debug)] +pub struct GuildMembersChunkEvent { + pub guild_id: GuildId, + pub members: HashMap<UserId, Member>, +} + +#[derive(Clone, Debug)] +pub struct GuildRoleCreateEvent { + pub guild_id: GuildId, + pub role: Role, +} + +#[derive(Clone, Debug)] +pub struct GuildRoleDeleteEvent { + pub guild_id: GuildId, + pub role_id: RoleId, +} + +#[derive(Clone, Debug)] +pub struct GuildRoleUpdateEvent { + pub guild_id: GuildId, + pub role: Role, +} + +#[derive(Clone, Debug)] +pub struct GuildSyncEvent { + pub guild_id: GuildId, + pub large: bool, + pub members: HashMap<UserId, Member>, + pub presences: HashMap<UserId, Presence>, +} + +#[derive(Clone, Debug)] +pub struct GuildUnavailableEvent { + pub guild_id: GuildId, +} + +#[derive(Clone, Debug)] +pub struct GuildUpdateEvent { + pub guild: Guild, +} + +#[derive(Clone, Copy, Debug)] +pub struct MessageAckEvent { + pub channel_id: ChannelId, + /// May be `None` if a private channel with no messages has closed. + pub message_id: Option<MessageId>, +} + +#[derive(Clone, Debug)] +pub struct MessageCreateEvent { + pub message: Message, +} + +#[derive(Clone, Debug)] +pub struct MessageDeleteBulkEvent { + pub channel_id: ChannelId, + pub ids: Vec<MessageId>, +} + +#[derive(Clone, Copy, Debug)] +pub struct MessageDeleteEvent { + pub channel_id: ChannelId, + pub message_id: MessageId, +} + +#[derive(Clone, Debug)] +pub struct MessageUpdateEvent { + pub id: MessageId, + pub channel_id: ChannelId, + pub kind: Option<MessageType>, + pub content: Option<String>, + pub nonce: Option<String>, + pub tts: Option<bool>, + pub pinned: Option<bool>, + pub timestamp: Option<String>, + pub edited_timestamp: Option<String>, + pub author: Option<User>, + pub mention_everyone: Option<bool>, + pub mentions: Option<Vec<User>>, + pub mention_roles: Option<Vec<RoleId>>, + pub attachments: Option<Vec<Attachment>>, + pub embeds: Option<Vec<Value>>, +} + +#[derive(Clone, Debug)] +pub struct PresenceUpdateEvent { + pub guild_id: Option<GuildId>, + pub presence: Presence, + pub roles: Option<Vec<RoleId>>, +} + +#[derive(Clone, Debug)] +pub struct PresencesReplaceEvent { + pub presences: Vec<Presence>, +} + +/// The "Ready" event, containing initial state +#[derive(Clone, Debug)] +pub struct ReadyEvent { + pub ready: Ready, +} + +#[derive(Clone, Debug)] +pub struct RelationshipAddEvent { + pub relationship: Relationship, +} + +#[derive(Clone, Copy, Debug)] +pub struct RelationshipRemoveEvent { + pub kind: RelationshipType, + pub user_id: UserId, +} + +#[derive(Clone, Debug)] +pub struct ResumedEvent { + pub heartbeat_interval: u64, + pub trace: Vec<Option<String>>, +} + +#[derive(Clone, Debug)] +pub struct TypingStartEvent { + pub channel_id: ChannelId, + pub timestamp: u64, + pub user_id: UserId, +} + +#[derive(Clone, Debug)] +pub struct UnknownEvent { + pub kind: String, + pub value: BTreeMap<String, Value> +} + +#[derive(Clone, Debug)] +pub struct UserGuildSettingsUpdateEvent { + pub settings: UserGuildSettings, +} + +#[derive(Clone, Debug)] +pub struct UserNoteUpdateEvent { + pub note: String, + pub user_id: UserId, +} + +#[derive(Clone, Debug)] +pub struct UserUpdateEvent { + pub current_user: CurrentUser, +} + +#[derive(Clone, Debug)] +pub struct UserSettingsUpdateEvent { + pub enable_tts_command: Option<bool>, + pub inline_attachment_media: Option<bool>, + pub inline_embed_media: Option<bool>, + pub locale: Option<String>, + pub message_display_compact: Option<bool>, + pub render_embeds: Option<bool>, + pub show_current_game: Option<bool>, + pub theme: Option<String>, + pub convert_emoticons: Option<bool>, + pub friend_source_flags: Option<FriendSourceFlags>, +} + +#[derive(Clone, Debug)] +pub struct VoiceServerUpdateEvent { + pub channel_id: Option<ChannelId>, + pub endpoint: Option<String>, + pub guild_id: Option<GuildId>, + pub token: String, +} + +#[derive(Clone, Debug)] +pub struct VoiceStateUpdateEvent { + pub guild_id: Option<GuildId>, + pub voice_state: VoiceState, +} + +#[derive(Debug, Clone)] +pub enum GatewayEvent { + Dispatch(u64, Event), + Heartbeat(u64), + Reconnect, + InvalidateSession, + Hello(u64), + HeartbeatAck, +} + +impl GatewayEvent { + pub fn decode(value: Value) -> Result<Self> { + let mut value = try!(into_map(value)); + match req!(value.get("op").and_then(|x| x.as_u64())) { + 0 => Ok(GatewayEvent::Dispatch( + req!(try!(remove(&mut value, "s")).as_u64()), + try!(Event::decode( + try!(remove(&mut value, "t").and_then(into_string)), + try!(remove(&mut value, "d")) + )) + )), + 1 => Ok(GatewayEvent::Heartbeat(req!(try!(remove(&mut value, "s")).as_u64()))), + 7 => Ok(GatewayEvent::Reconnect), + 9 => Ok(GatewayEvent::InvalidateSession), + 10 => { + let mut data = try!(remove(&mut value, "d").and_then(into_map)); + let interval = req!(try!(remove(&mut data, "heartbeat_interval")).as_u64()); + Ok(GatewayEvent::Hello(interval)) + }, + 11 => Ok(GatewayEvent::HeartbeatAck), + _ => Err(Error::Decode("Unexpected opcode", Value::Object(value))), + } + } +} + +#[derive(Debug, Clone)] +pub enum VoiceEvent { + Handshake { + heartbeat_interval: u64, + port: u16, + ssrc: u32, + modes: Vec<String>, + }, + Ready { + mode: String, + secret_key: Vec<u8>, + }, + SpeakingUpdate { + user_id: UserId, + ssrc: u32, + speaking: bool, + }, + KeepAlive, + Unknown(u64, Value) +} + +impl VoiceEvent { + pub fn decode(value: Value) -> Result<VoiceEvent> { + let mut value = try!(into_map(value)); + + let op = req!(try!(remove(&mut value, "op")).as_u64()); + if op == 3 { + return Ok(VoiceEvent::KeepAlive) + } + + let mut value = try!(remove(&mut value, "d").and_then(into_map)); + if op == 2 { + missing!(value, VoiceEvent::Handshake { + heartbeat_interval: req!(try!(remove(&mut value, "heartbeat_interval")).as_u64()), + modes: try!(decode_array(try!(remove(&mut value, "modes")), into_string)), + port: req!(try!(remove(&mut value, "port")).as_u64()) as u16, + ssrc: req!(try!(remove(&mut value, "ssrc")).as_u64()) as u32, + }) + } else if op == 4 { + missing!(value, VoiceEvent::Ready { + mode: try!(remove(&mut value, "mode").and_then(into_string)), + secret_key: try!(decode_array(try!(remove(&mut value, "secret_key")), + |v| Ok(req!(v.as_u64()) as u8) + )), + }) + } else if op == 5 { + missing!(value, VoiceEvent::SpeakingUpdate { + user_id: try!(remove(&mut value, "user_id").and_then(UserId::decode)), + ssrc: req!(try!(remove(&mut value, "ssrc")).as_u64()) as u32, + speaking: req!(try!(remove(&mut value, "speaking")).as_bool()), + }) + } else { + Ok(VoiceEvent::Unknown(op, Value::Object(value))) + } + } +} + +/// Event received over a websocket connection +#[derive(Clone, Debug)] +pub enum Event { + /// A new group call has been created + CallCreate(CallCreateEvent), + /// A group call has been deleted (the call ended) + CallDelete(CallDeleteEvent), + /// A group call has been updated + CallUpdate(CallUpdateEvent), + ChannelCreate(ChannelCreateEvent), + ChannelDelete(ChannelDeleteEvent), + ChannelPinsAck(ChannelPinsAckEvent), + ChannelPinsUpdate(ChannelPinsUpdateEvent), + /// A user has been added to a group + ChannelRecipientAdd(ChannelRecipientAddEvent), + /// A user has been removed from a group + ChannelRecipientRemove(ChannelRecipientRemoveEvent), + ChannelUpdate(ChannelUpdateEvent), + GuildBanAdd(GuildBanAddEvent), + GuildBanRemove(GuildBanRemoveEvent), + GuildCreate(GuildCreateEvent), + GuildDelete(GuildDeleteEvent), + GuildEmojisUpdate(GuildEmojisUpdateEvent), + GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent), + GuildMemberAdd(GuildMemberAddEvent), + GuildMemberRemove(GuildMemberRemoveEvent), + /// A member's roles have changed + GuildMemberUpdate(GuildMemberUpdateEvent), + GuildMembersChunk(GuildMembersChunkEvent), + GuildRoleCreate(GuildRoleCreateEvent), + GuildRoleDelete(GuildRoleDeleteEvent), + GuildRoleUpdate(GuildRoleUpdateEvent), + GuildSync(GuildSyncEvent), + /// When a guild is unavailable, such as due to a Discord server outage. + GuildUnavailable(GuildUnavailableEvent), + GuildUpdate(GuildUpdateEvent), + /// Another logged-in device acknowledged this message + MessageAck(MessageAckEvent), + MessageCreate(MessageCreateEvent), + MessageDelete(MessageDeleteEvent), + MessageDeleteBulk(MessageDeleteBulkEvent), + /// A message has been edited, either by the user or the system + MessageUpdate(MessageUpdateEvent), + /// A member's presence state (or username or avatar) has changed + PresenceUpdate(PresenceUpdateEvent), + /// The precense list of the user's friends should be replaced entirely + PresencesReplace(PresencesReplaceEvent), + /// The first event in a connection, containing the initial state. + /// + /// May also be received at a later time in the event of a reconnect. + Ready(ReadyEvent), + RelationshipAdd(RelationshipAddEvent), + RelationshipRemove(RelationshipRemoveEvent), + /// The connection has successfully resumed after a disconnect. + Resumed(ResumedEvent), + /// A user is typing; considered to last 5 seconds + TypingStart(TypingStartEvent), + /// Update to the logged-in user's guild-specific notification settings + UserGuildSettingsUpdate(UserGuildSettingsUpdateEvent), + /// Update to a note that the logged-in user has set for another user. + UserNoteUpdate(UserNoteUpdateEvent), + /// Update to the logged-in user's information + UserUpdate(UserUpdateEvent), + /// Update to the logged-in user's preferences or client settings + UserSettingsUpdate(UserSettingsUpdateEvent), + /// A member's voice state has changed + VoiceStateUpdate(VoiceStateUpdateEvent), + /// Voice server information is available + VoiceServerUpdate(VoiceServerUpdateEvent), + /// An event type not covered by the above + Unknown(UnknownEvent), +} + +impl Event { + #[allow(cyclomatic_complexity)] + fn decode(kind: String, value: Value) -> Result<Event> { + if kind == "PRESENCES_REPLACE" { + return Ok(Event::PresencesReplace(PresencesReplaceEvent { + presences: try!(decode_array(value, Presence::decode)), + })); + } + + let mut value = try!(into_map(value)); + + if kind == "CALL_CREATE" { + Ok(Event::CallCreate(CallCreateEvent { + call: try!(Call::decode(Value::Object(value))), + })) + } else if kind == "CALL_DELETE" { + missing!(value, Event::CallDelete(CallDeleteEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + })) + } else if kind == "CALL_UPDATE" { + missing!(value, Event::CallUpdate(CallUpdateEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + message_id: try!(remove(&mut value, "message_id").and_then(MessageId::decode)), + region: try!(remove(&mut value, "region").and_then(into_string)), + ringing: try!(decode_array(try!(remove(&mut value, "ringing")), UserId::decode)), + })) + } else if kind == "CHANNEL_CREATE" { + Ok(Event::ChannelCreate(ChannelCreateEvent { + channel: try!(Channel::decode(Value::Object(value))), + })) + } else if kind == "CHANNEL_DELETE" { + Ok(Event::ChannelDelete(ChannelDeleteEvent { + channel: try!(Channel::decode(Value::Object(value))), + })) + } else if kind == "CHANNEL_PINS_ACK" { + missing!(value, Event::ChannelPinsAck(ChannelPinsAckEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + timestamp: try!(remove(&mut value, "timestamp").and_then(into_string)), + })) + } else if kind == "CHANNEL_PINS_UPDATE" { + missing!(value, Event::ChannelPinsUpdate(ChannelPinsUpdateEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + last_pin_timestamp: try!(opt(&mut value, "last_pin_timestamp", into_string)), + })) + } else if kind == "CHANNEL_RECIPIENT_ADD" { + missing!(value, Event::ChannelRecipientAdd(ChannelRecipientAddEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "CHANNEL_RECIPIENT_REMOVE" { + missing!(value, Event::ChannelRecipientRemove(ChannelRecipientRemoveEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "CHANNEL_UPDATE" { + Ok(Event::ChannelUpdate(ChannelUpdateEvent { + channel: try!(Channel::decode(Value::Object(value))), + })) + } else if kind == "GUILD_BAN_ADD" { + missing!(value, Event::GuildBanAdd(GuildBanAddEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "GUILD_BAN_REMOVE" { + missing!(value, Event::GuildBanRemove(GuildBanRemoveEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "GUILD_CREATE" { + if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) { + Ok(Event::GuildUnavailable(GuildUnavailableEvent { + guild_id: try!(remove(&mut value, "id").and_then(GuildId::decode)), + })) + } else { + Ok(Event::GuildCreate(GuildCreateEvent { + guild: try!(LiveGuild::decode(Value::Object(value))), + })) + } + } else if kind == "GUILD_DELETE" { + if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) { + Ok(Event::GuildUnavailable(GuildUnavailableEvent { + guild_id: try!(remove(&mut value, "id").and_then(GuildId::decode)), + })) + } else { + Ok(Event::GuildDelete(GuildDeleteEvent { + guild: try!(Guild::decode(Value::Object(value))), + })) + } + } else if kind == "GUILD_EMOJIS_UPDATE" { + missing!(value, Event::GuildEmojisUpdate(GuildEmojisUpdateEvent { + emojis: try!(remove(&mut value, "emojis").and_then(decode_emojis)), + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + })) + } else if kind == "GUILD_INTEGRATIONS_UPDATE" { + missing!(value, Event::GuildIntegrationsUpdate(GuildIntegrationsUpdateEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + })) + } else if kind == "GUILD_MEMBER_ADD" { + Ok(Event::GuildMemberAdd(GuildMemberAddEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + member: try!(Member::decode(Value::Object(value))), + })) + } else if kind == "GUILD_MEMBER_REMOVE" { + missing!(value, Event::GuildMemberRemove(GuildMemberRemoveEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "GUILD_MEMBER_UPDATE" { + missing!(value, Event::GuildMemberUpdate(GuildMemberUpdateEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + nick: try!(opt(&mut value, "nick", into_string)), + roles: try!(decode_array(try!(remove(&mut value, "roles")), RoleId::decode)), + user: try!(remove(&mut value, "user").and_then(User::decode)), + })) + } else if kind == "GUILD_MEMBERS_CHUNK" { + missing!(value, Event::GuildMembersChunk(GuildMembersChunkEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + members: try!(remove(&mut value, "members").and_then(decode_members)), + })) + } else if kind == "GUILD_ROLE_CREATE" { + missing!(value, Event::GuildRoleCreate(GuildRoleCreateEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + role: try!(remove(&mut value, "role").and_then(Role::decode)), + })) + } else if kind == "GUILD_ROLE_DELETE" { + missing!(value, Event::GuildRoleDelete(GuildRoleDeleteEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + role_id: try!(remove(&mut value, "role_id").and_then(RoleId::decode)), + })) + } else if kind == "GUILD_ROLE_UPDATE" { + missing!(value, Event::GuildRoleUpdate(GuildRoleUpdateEvent { + guild_id: try!(remove(&mut value, "guild_id").and_then(GuildId::decode)), + role: try!(remove(&mut value, "role").and_then(Role::decode)), + })) + } else if kind == "GUILD_SYNC" { + missing!(value, Event::GuildSync(GuildSyncEvent { + guild_id: try!(remove(&mut value, "id").and_then(GuildId::decode)), + large: req!(try!(remove(&mut value, "large")).as_bool()), + members: try!(remove(&mut value, "members").and_then(decode_members)), + presences: try!(remove(&mut value, "presences").and_then(decode_presences)), + })) + } else if kind == "GUILD_UPDATE" { + Ok(Event::GuildUpdate(GuildUpdateEvent { + guild: try!(Guild::decode(Value::Object(value))), + })) + } else if kind == "MESSAGE_ACK" { + missing!(value, Event::MessageAck(MessageAckEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + message_id: try!(opt(&mut value, "message_id", MessageId::decode)), + })) + } else if kind == "MESSAGE_CREATE" { + Ok(Event::MessageCreate(MessageCreateEvent { + message: try!(Message::decode(Value::Object(value))), + })) + } else if kind == "MESSAGE_DELETE" { + missing!(value, Event::MessageDelete(MessageDeleteEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + message_id: try!(remove(&mut value, "id").and_then(MessageId::decode)), + })) + } else if kind == "MESSAGE_DELETE_BULK" { + missing!(value, Event::MessageDeleteBulk(MessageDeleteBulkEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + ids: try!(decode_array(try!(remove(&mut value, "ids")), MessageId::decode)), + })) + } else if kind == "MESSAGE_UPDATE" { + missing!(value, Event::MessageUpdate(MessageUpdateEvent { + id: try!(remove(&mut value, "id").and_then(MessageId::decode)), + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + kind: try!(opt(&mut value, "type", MessageType::decode)), + content: try!(opt(&mut value, "content", into_string)), + nonce: remove(&mut value, "nonce").and_then(into_string).ok(), + tts: remove(&mut value, "tts").ok().and_then(|v| v.as_bool()), + pinned: remove(&mut value, "pinned").ok().and_then(|v| v.as_bool()), + timestamp: try!(opt(&mut value, "timestamp", into_string)), + edited_timestamp: try!(opt(&mut value, "edited_timestamp", into_string)), + author: try!(opt(&mut value, "author", User::decode)), + mention_everyone: remove(&mut value, "mention_everyone").ok().and_then(|v| v.as_bool()), + mentions: try!(opt(&mut value, "mentions", |v| decode_array(v, User::decode))), + mention_roles: try!(opt(&mut value, "mention_roles", |v| decode_array(v, RoleId::decode))), + attachments: try!(opt(&mut value, "attachments", |v| decode_array(v, Attachment::decode))), + embeds: try!(opt(&mut value, "embeds", |v| decode_array(v, Ok))), + })) + } else if kind == "PRESENCE_UPDATE" { + let guild_id = try!(opt(&mut value, "guild_id", GuildId::decode)); + let roles = try!(opt(&mut value, "roles", |v| decode_array(v, RoleId::decode))); + let presence = try!(Presence::decode(Value::Object(value))); + Ok(Event::PresenceUpdate(PresenceUpdateEvent { + guild_id: guild_id, + presence: presence, + roles: roles, + })) + } else if kind == "RELATIONSHIP_ADD" { + Ok(Event::RelationshipAdd(RelationshipAddEvent { + relationship: try!(Relationship::decode(Value::Object(value))), + })) + } else if kind == "RELATIONSHIP_REMOVE" { + missing!(value, Event::RelationshipRemove(RelationshipRemoveEvent { + kind: try!(remove(&mut value, "type").and_then(RelationshipType::decode)), + user_id: try!(remove(&mut value, "id").and_then(UserId::decode)), + })) + } else if kind == "READY" { + Ok(Event::Ready(ReadyEvent { + ready: try!(Ready::decode(Value::Object(value))), + })) + } else if kind == "RESUMED" { + missing!(value, Event::Resumed(ResumedEvent { + heartbeat_interval: req!(try!(remove(&mut value, "heartbeat_interval")).as_u64()), + trace: try!(remove(&mut value, "_trace").and_then(|v| decode_array(v, |v| Ok(into_string(v).ok())))), + })) + } else if kind == "TYPING_START" { + missing!(value, Event::TypingStart(TypingStartEvent { + channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)), + timestamp: req!(try!(remove(&mut value, "timestamp")).as_u64()), + user_id: try!(remove(&mut value, "user_id").and_then(UserId::decode)), + })) + } else if kind == "USER_GUILD_SETTINGS_UPDATE" { + Ok(Event::UserGuildSettingsUpdate(UserGuildSettingsUpdateEvent { + settings: try!(UserGuildSettings::decode(Value::Object(value))), + })) + } else if kind == "USER_NOTE_UPDATE" { + missing!(value, Event::UserNoteUpdate(UserNoteUpdateEvent { + note: try!(remove(&mut value, "note").and_then(into_string)), + user_id: try!(remove(&mut value, "id").and_then(UserId::decode)), + })) + } else if kind == "USER_SETTINGS_UPDATE" { + missing!(value, Event::UserSettingsUpdate(UserSettingsUpdateEvent { + enable_tts_command: remove(&mut value, "enable_tts_command").ok().and_then(|v| v.as_bool()), + inline_attachment_media: remove(&mut value, "inline_attachment_media").ok().and_then(|v| v.as_bool()), + inline_embed_media: remove(&mut value, "inline_embed_media").ok().and_then(|v| v.as_bool()), + locale: try!(opt(&mut value, "locale", into_string)), + message_display_compact: remove(&mut value, "message_display_compact").ok().and_then(|v| v.as_bool()), + render_embeds: remove(&mut value, "render_embeds").ok().and_then(|v| v.as_bool()), + show_current_game: remove(&mut value, "show_current_game").ok().and_then(|v| v.as_bool()), + theme: try!(opt(&mut value, "theme", into_string)), + convert_emoticons: remove(&mut value, "convert_emoticons").ok().and_then(|v| v.as_bool()), + friend_source_flags: try!(opt(&mut value, "friend_source_flags", FriendSourceFlags::decode)), + })) + } else if kind == "USER_UPDATE" { + Ok(Event::UserUpdate(UserUpdateEvent { + current_user: try!(CurrentUser::decode(Value::Object(value))), + })) + } else if kind == "VOICE_SERVER_UPDATE" { + missing!(value, Event::VoiceServerUpdate(VoiceServerUpdateEvent { + guild_id: try!(opt(&mut value, "guild_id", GuildId::decode)), + channel_id: try!(opt(&mut value, "channel_id", ChannelId::decode)), + endpoint: try!(opt(&mut value, "endpoint", into_string)), + token: try!(remove(&mut value, "token").and_then(into_string)), + })) + } else if kind == "VOICE_STATE_UPDATE" { + Ok(Event::VoiceStateUpdate(VoiceStateUpdateEvent { + guild_id: try!(opt(&mut value, "guild_id", GuildId::decode)), + voice_state: try!(VoiceState::decode(Value::Object(value))), + })) + } else { + Ok(Event::Unknown(UnknownEvent { + kind: kind, + value: value, + })) + } + } +} + +impl Game { + pub fn playing(name: String) -> Game { + Game { + kind: GameType::Playing, + name: name, + url: None, + } + } + + pub fn streaming(name: String, url: String) -> Game { + Game { + kind: GameType::Streaming, + name: name, + url: Some(url), + } + } + + pub fn decode(value: Value) -> Result<Option<Game>> { + let mut map = try!(into_map(value)); + + let name = match map.remove("name") { + Some(Value::Null) | None => return Ok(None), + Some(v) => try!(into_string(v)), + }; + + if name.trim().is_empty() { + return Ok(None); + } + + missing!(map, Some(Game { + name: name, + kind: try!(opt(&mut map, "type", GameType::decode)).unwrap_or(GameType::Playing), + url: try!(opt(&mut map, "url", into_string)), + })) + } +} + +impl Presence { + pub fn decode(value: Value) -> Result<Presence> { + let mut value = try!(into_map(value)); + let mut user_map = try!(remove(&mut value, "user").and_then(into_map)); + + let (user_id, user) = if user_map.len() > 1 { + let user = try!(User::decode(Value::Object(user_map))); + (user.id, Some(user)) + } else { + (try!(remove(&mut user_map, "id").and_then(UserId::decode)), None) + }; + + let game = match value.remove("game") { + None | Some(Value::Null) => None, + Some(v) => try!(Game::decode(v)), + }; + + missing!(value, Presence { + user_id: user_id, + status: try!(remove(&mut value, "status").and_then(OnlineStatus::decode_str)), + last_modified: try!(opt(&mut value, "last_modified", |v| Ok(req!(v.as_u64())))), + game: game, + user: user, + nick: try!(opt(&mut value, "nick", into_string)), + }) + } +} |