1 /** 2 Implementations of packets sent over the Gateway websocket. 3 */ 4 module dscord.gateway.packets; 5 6 import std.stdio; 7 8 import dscord.types; 9 10 enum OPCode : ushort { 11 DISPATCH = 0, 12 HEARTBEAT = 1, 13 IDENTIFY = 2, 14 STATUS_UPDATE = 3, 15 VOICE_STATE_UPDATE = 4, 16 VOICE_SERVER_PING = 5, 17 RESUME = 6, 18 RECONNECT = 7, 19 REQUEST_GUILD_MEMBERS = 8, 20 INVALID_SESSION = 9, 21 HELLO = 10, 22 HEARTBEAT_ACK = 11, 23 GUILD_SYNC = 12, 24 }; 25 26 interface Serializable { 27 VibeJSON serialize(); 28 } 29 30 interface Deserializable { 31 } 32 33 class BasePacket { 34 OPCode op; 35 VibeJSON data; 36 VibeJSON raw; 37 38 VibeJSON serialize(ushort op, VibeJSON data) { 39 return VibeJSON([ 40 "op": VibeJSON(op), 41 "d": data, 42 ]); 43 } 44 } 45 46 class HeartbeatPacket : BasePacket, Serializable { 47 uint seq; 48 49 this(uint seq) { 50 this.seq = seq; 51 } 52 53 override VibeJSON serialize() { 54 return super.serialize(OPCode.HEARTBEAT, VibeJSON(this.seq)); 55 } 56 } 57 58 class ResumePacket : BasePacket, Serializable { 59 string token; 60 string sessionID; 61 uint seq; 62 63 this(string token, string sessionID, uint seq) { 64 this.token = token; 65 this.sessionID = sessionID; 66 this.seq = seq; 67 } 68 69 override VibeJSON serialize() { 70 return super.serialize(OPCode.RESUME, VibeJSON([ 71 "token": VibeJSON(this.token), 72 "session_id": VibeJSON(this.sessionID), 73 "seq": VibeJSON(this.seq), 74 ])); 75 } 76 } 77 78 class VoiceStateUpdatePacket : BasePacket, Serializable { 79 Snowflake guildID; 80 Snowflake channelID; 81 bool self_mute; 82 bool self_deaf; 83 84 this(Snowflake guild_id, Snowflake channel_id, bool self_mute, bool self_deaf) { 85 this.guildID = guild_id; 86 this.channelID = channel_id; 87 this.self_mute = self_mute; 88 this.self_deaf = self_deaf; 89 } 90 91 override VibeJSON serialize() { 92 return super.serialize(OPCode.VOICE_STATE_UPDATE, VibeJSON([ 93 "self_mute": VibeJSON(this.self_mute), 94 "self_deaf": VibeJSON(this.self_deaf), 95 "guild_id": this.guildID ? VibeJSON(this.guildID) : VibeJSON(null), 96 "channel_id": this.channelID ? VibeJSON(this.channelID) : VibeJSON(null), 97 ])); 98 } 99 } 100 101 class IdentifyPacket : BasePacket, Serializable { 102 string token; 103 bool compress = true; 104 ushort largeThreshold = 250; 105 ushort[2] shard; 106 107 this(string token, ushort shard = 0, ushort numShards = 1) { 108 this.token = token; 109 this.shard = [shard, numShards]; 110 } 111 112 @property VibeJSON properties() { 113 return VibeJSON([ 114 "$os": VibeJSON("linux"), 115 "$browser": VibeJSON("dscord"), 116 "$device": VibeJSON("dscord"), 117 "$referrer": VibeJSON(""), 118 ]); 119 } 120 121 override VibeJSON serialize() { 122 return super.serialize(OPCode.IDENTIFY, VibeJSON([ 123 "token": VibeJSON(this.token), 124 "properties": this.properties, 125 "compress": VibeJSON(this.compress), 126 "large_threshold": VibeJSON(this.largeThreshold), 127 "shard": VibeJSON([VibeJSON(this.shard[0]), VibeJSON(this.shard[1])]), 128 ])); 129 } 130 } 131 132 class RequestGuildMembers : BasePacket, Serializable { 133 Snowflake guildID; 134 string query; 135 uint limit; 136 137 this(Snowflake guildID, string query="", uint limit=0) { 138 this.guildID = guildID; 139 this.query = query; 140 this.limit = limit; 141 } 142 143 override VibeJSON serialize() { 144 return super.serialize(OPCode.REQUEST_GUILD_MEMBERS, VibeJSON([ 145 "guild_id": VibeJSON(this.guildID.toString), 146 "query": VibeJSON(this.query), 147 "limit": VibeJSON(this.limit), 148 ])); 149 } 150 } 151 152 class StatusUpdate : BasePacket, Serializable { 153 uint idleSince; 154 Game game; 155 156 this(uint idleSince=0, Game game=null) { 157 this.idleSince = idleSince; 158 this.game = game; 159 160 } 161 162 override VibeJSON serialize() { 163 VibeJSON obj = VibeJSON.emptyObject; 164 165 if (this.idleSince > 0) { 166 obj["idle_since"] = VibeJSON(this.idleSince); 167 } else { 168 obj["idle_since"] = VibeJSON(null); 169 } 170 171 if (this.game) { 172 obj["game"] = this.game.dump(); 173 } else { 174 obj["game"] = VibeJSON.emptyObject; 175 } 176 177 return super.serialize(OPCode.STATUS_UPDATE, obj); 178 } 179 }