use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::model::channel::Channel;
use crate::model::emoji::{Emoji, PartialEmoji};
use crate::model::guild::{Guild, Member, Role};
use crate::model::id::{ChannelId, GuildId, MessageId, RoleId, UserId};
use crate::model::message::Message;
use crate::model::sticker::Sticker;
use crate::model::user::User;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum Opcode {
Dispatch = 0,
Heartbeat = 1,
Identify = 2,
PresenceUpdate = 3,
VoiceStateUpdate = 4,
Resume = 6,
Reconnect = 7,
InvalidSession = 9,
Hello = 10,
HeartbeatAck = 11,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GatewayPayload {
pub op: Opcode,
#[serde(default)]
pub d: Option<Value>,
#[serde(default)]
pub s: Option<u64>,
#[serde(default)]
pub t: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HelloPayload {
pub heartbeat_interval: u64,
}
#[derive(Debug, Clone, Serialize)]
pub struct IdentifyPayload {
pub token: String,
pub properties: IdentifyProperties,
}
#[derive(Debug, Clone, Serialize)]
pub struct IdentifyProperties {
pub os: String,
pub browser: String,
pub device: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct ResumePayload {
pub token: String,
pub session_id: String,
pub seq: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ReadyPayload {
pub user: User,
pub session_id: String,
pub guilds: Vec<Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ResumedPayload {
#[serde(default)]
pub _trace: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageDeletePayload {
pub id: MessageId,
pub channel_id: ChannelId,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildDeletePayload {
pub id: GuildId,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildMemberAddPayload {
pub guild_id: GuildId,
#[serde(flatten)]
pub member: Member,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildMemberRemovePayload {
pub guild_id: GuildId,
pub user: User,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TypingStartPayload {
pub channel_id: ChannelId,
pub user_id: UserId,
pub timestamp: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageReactionAddPayload {
pub user_id: UserId,
pub channel_id: ChannelId,
pub message_id: MessageId,
#[serde(default)]
pub guild_id: Option<GuildId>,
#[serde(default)]
pub member: Option<Member>,
pub emoji: PartialEmoji,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageReactionRemovePayload {
pub user_id: UserId,
pub channel_id: ChannelId,
pub message_id: MessageId,
#[serde(default)]
pub guild_id: Option<GuildId>,
pub emoji: PartialEmoji,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageReactionRemoveAllPayload {
pub channel_id: ChannelId,
pub message_id: MessageId,
#[serde(default)]
pub guild_id: Option<GuildId>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageReactionRemoveEmojiPayload {
pub channel_id: ChannelId,
pub message_id: MessageId,
#[serde(default)]
pub guild_id: Option<GuildId>,
pub emoji: PartialEmoji,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildRoleCreatePayload {
pub guild_id: GuildId,
pub role: Role,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildRoleUpdatePayload {
pub guild_id: GuildId,
pub role: Role,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildRoleDeletePayload {
pub guild_id: GuildId,
pub role_id: RoleId,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildBanAddPayload {
pub guild_id: GuildId,
pub user: User,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildBanRemovePayload {
pub guild_id: GuildId,
pub user: User,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildMemberUpdatePayload {
pub guild_id: GuildId,
pub user: User,
#[serde(default)]
pub roles: Vec<RoleId>,
#[serde(default)]
pub nick: Option<String>,
#[serde(default)]
pub joined_at: Option<String>,
#[serde(default)]
pub premium_since: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ChannelPinsUpdatePayload {
#[serde(default)]
pub guild_id: Option<GuildId>,
pub channel_id: ChannelId,
#[serde(default)]
pub last_pin_timestamp: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct MessageDeleteBulkPayload {
pub ids: Vec<MessageId>,
pub channel_id: ChannelId,
#[serde(default)]
pub guild_id: Option<GuildId>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct InviteCreatePayload {
pub channel_id: ChannelId,
pub code: String,
#[serde(default)]
pub guild_id: Option<GuildId>,
#[serde(default)]
pub inviter: Option<User>,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
pub max_age: Option<u64>,
#[serde(default)]
pub max_uses: Option<u64>,
#[serde(default)]
pub temporary: bool,
#[serde(default)]
pub uses: u64,
}
#[derive(Debug, Clone, Deserialize)]
pub struct InviteDeletePayload {
pub channel_id: ChannelId,
pub code: String,
#[serde(default)]
pub guild_id: Option<GuildId>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhooksUpdatePayload {
pub guild_id: GuildId,
pub channel_id: ChannelId,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildEmojisUpdatePayload {
pub guild_id: GuildId,
pub emojis: Vec<Emoji>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GuildStickersUpdatePayload {
pub guild_id: GuildId,
pub stickers: Vec<Sticker>,
}
macro_rules! gateway_events {
( $( $event_name:literal => $variant:ident ( $payload:ty ) ),* $(,)? ) => {
#[derive(Debug, Clone)]
pub enum GatewayEvent {
$( $variant($payload), )*
Unknown {
event: String,
data: Value,
},
}
impl GatewayEvent {
pub fn from_dispatch(event_name: &str, data: Value) -> Self {
let result: Result<Self, serde_json::Error> = match event_name {
$(
$event_name => serde_json::from_value(data.clone()).map(Self::$variant),
)*
_ => return Self::Unknown { event: event_name.to_string(), data },
};
match result {
Ok(event) => event,
Err(e) => {
tracing::warn!(
event = event_name,
error = %e,
"failed to deserialize dispatch event, treating as Unknown",
);
Self::Unknown { event: event_name.to_string(), data }
}
}
}
}
};
}
gateway_events! {
"READY" => Ready(ReadyPayload),
"RESUMED" => Resumed(ResumedPayload),
"USER_UPDATE" => UserUpdate(User),
"MESSAGE_CREATE" => MessageCreate(Message),
"MESSAGE_UPDATE" => MessageUpdate(Message),
"MESSAGE_DELETE" => MessageDelete(MessageDeletePayload),
"MESSAGE_DELETE_BULK" => MessageDeleteBulk(MessageDeleteBulkPayload),
"MESSAGE_REACTION_ADD" => MessageReactionAdd(MessageReactionAddPayload),
"MESSAGE_REACTION_REMOVE" => MessageReactionRemove(MessageReactionRemovePayload),
"MESSAGE_REACTION_REMOVE_ALL" => MessageReactionRemoveAll(MessageReactionRemoveAllPayload),
"MESSAGE_REACTION_REMOVE_EMOJI" => MessageReactionRemoveEmoji(MessageReactionRemoveEmojiPayload),
"GUILD_CREATE" => GuildCreate(Guild),
"GUILD_UPDATE" => GuildUpdate(Guild),
"GUILD_DELETE" => GuildDelete(GuildDeletePayload),
"GUILD_MEMBER_ADD" => GuildMemberAdd(GuildMemberAddPayload),
"GUILD_MEMBER_UPDATE" => GuildMemberUpdate(GuildMemberUpdatePayload),
"GUILD_MEMBER_REMOVE" => GuildMemberRemove(GuildMemberRemovePayload),
"GUILD_ROLE_CREATE" => GuildRoleCreate(GuildRoleCreatePayload),
"GUILD_ROLE_UPDATE" => GuildRoleUpdate(GuildRoleUpdatePayload),
"GUILD_ROLE_DELETE" => GuildRoleDelete(GuildRoleDeletePayload),
"GUILD_BAN_ADD" => GuildBanAdd(GuildBanAddPayload),
"GUILD_BAN_REMOVE" => GuildBanRemove(GuildBanRemovePayload),
"CHANNEL_CREATE" => ChannelCreate(Channel),
"CHANNEL_UPDATE" => ChannelUpdate(Channel),
"CHANNEL_DELETE" => ChannelDelete(Channel),
"CHANNEL_PINS_UPDATE" => ChannelPinsUpdate(ChannelPinsUpdatePayload),
"TYPING_START" => TypingStart(TypingStartPayload),
"INVITE_CREATE" => InviteCreate(InviteCreatePayload),
"INVITE_DELETE" => InviteDelete(InviteDeletePayload),
"WEBHOOKS_UPDATE" => WebhooksUpdate(WebhooksUpdatePayload),
"GUILD_EMOJIS_UPDATE" => GuildEmojisUpdate(GuildEmojisUpdatePayload),
"GUILD_STICKERS_UPDATE" => GuildStickersUpdate(GuildStickersUpdatePayload),
}